SQLインジェクション

パラメーターのバインドで入力値とSQL構文を分離する

説明

外部入力がデータではなくSQL構文の一部として解釈されると、SQLインジェクションが発生します。攻撃者はクエリの条件や操作を変更できます。影響はデータベースアカウントの権限と利用できる機能によって変わります。

想定される影響

  • データベースの機能と権限が許す場合のOSコマンド実行
  • データベース内の機密情報の漏えい
  • なりすましや権限昇格
  • 認証・アクセス制御の回避
  • データの不正な変更や削除

対処方法

  1. PreparedStatementやJdbcTemplateのパラメーターバインドで、値とSQL構文を分けてください。入力を連結してから準備するだけでは不十分です。
  2. ORMでも、SQL・JPQL・HQLを文字列で組み立てると同じ危険があります。対応するバインドAPIを使ってください。
  3. テーブル名、列名、並べ替え方向など値パラメーターにできない要素は、サーバーが管理する許可リストから選んでください。
  4. 入力の形式と長さを検証し、データベースアカウントには必要最小限の権限を付与してください。

例

JdbcTemplateでユーザーレコードを取得する抜粋であり、ログイン実装ではありません。パスワード検証とレコードを読む権限の確認は別途行ってください。

変更前

java
String query = "SELECT id, username FROM users WHERE username = '" + username + "'";
jdbcTemplate.queryForList(query);

変更後

java
String query = "SELECT id, username FROM users WHERE username = ?";
jdbcTemplate.queryForList(query, username);

変更前はusernameをSQL文字列に連結します。変更後は検索用のqueryForList()に値を分けて渡すため、usernameがSQL構文として解釈されません。

関連CVE

参考資料