SWEet

A Software Engineer Is Eating Technologies

SQLインジェクション対策

午後問でSQLインジェクションに対する防御プログラミングの問題が出ていたのでまとめてみました。

  • SQLインジェクションとは何か?

    ユーザーの入力データをもとにSQL文を編集してDBにクエリを発行し、その結果を反映させるというwebページにおいて、不正なSQL文を入力することでDBを操作したり、DBに登録された情報を不正に取得、変更するといった攻撃手法です。XSS同様にユーザーの入力データのチェック不備から成立する可能性がりあます。

今回はjavaのソースを基にSQLインジェクションを成立させてしまうコードとそれを防ぐコードを簡単にまとめました。

      String sqlString = "SELECT * FROM db_user WHERE username = '"
                         + username +
                         "' AND password = '" + pwd + "'";
      Statement stmt = connection.createStatement();
      ResultSet rs = stmt.executeQuery(sqlString);

  if (!rs.next()) {
    throw new SecurityException(
      "User name or password incorrect"
    );
  }

これは脆弱性のあるコードを一部抜粋したものです。このコードは入力された文字列を結合してSQL文としてクエリしています。 例として、入力部分のusername pwd に 次の様な入力を与えたとします。

 username = katsuya , pwd = ' OR 'A' = 'A

メジャーなSQLインジェクションです。上記のような入力の場合実行されるSQL文は以下のようになります。

SELECT * FROM db_user WHERE username = 'katsuya' AND password = '' OR 'A' = 'A'

入力したOR文があります。この入力によってパスワードは特に値を与えていないのですがその後ろにあるOR文の条件は常に真なので db_userのレコードが全て選択されることになります

恐ろしいです。しかし、しっかりと対策をすることで防ぐことはできます。以下が対策を施したソースです。

String sqlString =
        "select * from db_user where username=? and password=?";
      PreparedStatement stmt = connection.prepareStatement(sqlString);
      stmt.setString(1, username);
      stmt.setString(2, pwd);
      ResultSet rs = stmt.executeQuery();
      if (!rs.next()) {
        throw new SecurityException("User name or password incorrect");
      }

これprepareStatementメソッドを使った対策です。これを使用することによってユーザーの入力は特別な文字列( $ ^ ' " 等)が含まれていても自動でエスケープ処理されてただの文字列として扱われます。

他の対策としては、そもそものRDBMSのアクセス権限を最小にするといったものがあります。

似たような攻撃としてはOSコマンドインジェクションやディレクトリバーサル攻撃が挙げられます。どちらもユーザーが悪意のあるスクリプトを送信して本来は権限のない行為を実行しようとするものです。

しかし、最近のwebアプリケーションフレームワークはデフォルトで対策されているものが殆どなので超個人用のアプリでもない限り実現できる攻撃ではないのかなと思っています。

SCの過去問ではセキュアプログラミングの題材として何回か出題実績があったのでまとめてみました。 今回はこのあたりで失礼します。