1. ホーム
  2. データベース
  3. その他のデータベース

SQLインジェクションの例とその解決方法

2022-01-18 22:02:11

前文

SQLインジェクションとは、Webアプリケーションがユーザーの入力データの正当性を判断していない、または厳密なフィルタリングを行っていないため、攻撃者がWebアプリケーションであらかじめ定義したクエリ文の末尾にSQL文を追加して管理者の知らないところで不正操作を実現し、データベースサーバーを騙して不正な任意のクエリを実行させ、さらに該当するデータ情報を取得する目的を達成することを意味します。

1. SQLインジェクションの例

コンソールでユーザー名とパスワードを入力し、Statementの文字列連結でログインさせるというSQLインジェクションのケースをシミュレートしています。

1.1 データベースにユーザーテーブルとデータを最初に作成する

-- Create a table of users
CREATE TABLE `users` (
 `id` INT(11) NOT NULL AUTO_INCREMENT,
 `username` VARCHAR(20),
 `password` VARCHAR(50),
 PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

-- Insert data
INSERT INTO users(username,`password`) VALUES('Zhang Fei','123321'),('Zhao Yun','qazxsw'),('Zhuge Liang','123Qwe');
INSERT INTO users(username,`password`) VALUES('Cao Cao','741258'),('Liu Bei','plmokn'),('Sun Quan','! @#$%^');

-- View data
SELECT * FROM users;

1.2 ログインプログラムを作成する

package com.study.task0201;

import java.sql.*;
import java.util.Scanner;

public class TestSQLIn {
 public static void main(String[] args) throws ClassNotFoundException, SQLException {
  Class.forName("com.mysql.jdbc.Driver");
  String url = "jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=UTF-8";
  Connection conn = DriverManager.getConnection(url,"root","123456");
  // System.out.println(conn);
  // Get the statement execution platform object Statement
  Statement smt = conn.createStatement();

  Scanner sc = new Scanner(System.in);
  System.out.println("Please enter the user name: ");
  String userName = sc.nextLine();
  System.out.println("Please enter the password: ");
  String password = sc.nextLine();

  String sql = "select * from users where username = '" + userName + "' and password = '" + password +"'"; //print out the SQL
  System.out.println(sql);
  ResultSet resultSet = smt.executeQuery(sql);
  if(resultSet.next()){
   System.out.println("Login successful!!! ");
  }else{
   System.out.println("Username or password error, please re-enter!!! ");
  }

  resultSet.close();
  smt.close();
  conn.close();

 }

}

1.3 通常ログイン

正しいユーザー名とパスワードを入力すると、"Login successful"が表示されます。

1.4 ログイン失敗

ユーザー名またはパスワードを間違って入力した場合、「ユーザー名またはパスワードが間違っています、再入力してください"」が表示されます。

1.5 SQLインジェクションのシミュレーション

スプライスされた文字列は、定数条件として or '1'='1' を持つので、前のユーザとパスワードが存在しなくても、すべてのレコードが取得され、 "Login successful" と表示されます。

1.6 SQL構文エラー報告

スプライシングでは、SQL構文エラーなどのエラーも発生します、例えば

2. 解決方法

ステートメント方式では、文字列のスプライシングによって元のSQLの真意を変えることができるため、SQLインジェクションの危険性があります。SQLインジェクションを解決するには、Statementの代わりに前処理オブジェクトであるPreparedStatementを使用して処理することができます。

2.1 プロシージャ

import java.sql.*;
import java.util.Scanner;

public class TestSQLIn {
 public static void main(String[] args) throws ClassNotFoundException, SQLException {
  Class.forName("com.mysql.jdbc.Driver");
  String url = "jdbc:mysql://127.0.0.1:3306/testdb?characterEncoding=UTF-8";
  Connection conn = DriverManager.getConnection(url,"root","123456");
  // System.out.println(conn);
  // Get the statement execution platform object Statement
  // Statement smt = conn.createStatement();

  Scanner sc = new Scanner(System.in);
  System.out.println("Please enter the user name: ");
  String userName = sc.nextLine();
  System.out.println("Please enter the password: ");
  String password = sc.nextLine();

  String sql = "select * from users where username = ? and password = ? ";
  // System.out.println(sql);
  // ResultSet resultSet = smt.executeQuery(sql);
  PreparedStatement preparedStatement = conn.preparedStatement(sql);
  preparedStatement.setString(1,userName);
  preparedStatement.setString(2,password);

  ResultSet resultSet = preparedStatement.executeQuery();
  if(resultSet.next()){
   System.out.println("Login successful!!! ");
  }else{
   System.out.println("Username or password error, please re-enter!!! ");
  }


  preparedStatement.close();
  resultSet.close();
  // smt.close();
  conn.close();

 }

}

2.2 通常ログイン

2.3 ユーザー名・パスワードのエラー

ユーザー名またはパスワードの入力に誤りがあった場合、「"ユーザー名またはパスワードが正しくありません、再入力してください"」と表示されます。

2.4 SQLインジェクションのシミュレーション

以前と同じようにSQLインジェクションを記述し、テスト後にSQLインジェクションが発生しなくなる。

2.5 SQL構文エラーのシミュレート

プリプロセッサクラスを使用する際、シングルクォートまたはダブルクォートを使用して入力しても、SQL 構文エラーにならなくなりました。

3. 概要

StatementとPreparedStatementの主な相違点は以下の通りです。

  • ステートメントは、静的なSQL文を実行するために使用され、事前に準備されたSQL文は、実行時に指定する必要があります。
  • PrepareStatementはコンパイル済みのSQL文オブジェクトで、実行時に"?"パラメータ値を動的に設定することが可能です。
  • PrepareStatementはコンパイル時間を短縮し、データベースのパフォーマンスを向上させることができる

サマライズ

この記事は、SQLインジェクションとそれを解決する方法について紹介し、より関連するSQLインジェクションと解決策の内容は、スクリプトハウスの過去の記事を検索してくださいまたは次の関連記事を閲覧し続けることは、今後、よりスクリプトハウスをサポートして願っています!.