はじめに
想定読者
- SQLインジェクションという言葉は知ってるけど、どういうものなのか知らない人
検証環境
Mac
Java17
SpringBoot3.1.0
SQLインジェクション
概要
SQLの呼び出し方に不備によって起こる脆弱性です。
攻撃者が任意のSQLを実行することができるため、以下のようなことが可能となります。
- データの更新や削除
- 取得できないはずのデータの取得
ピンと来ていない方は以下の画像を見てください。
検索ボックスのユーザ名と一致するユーザしか取得できないのですが、' OR '1' = '1
と入力することで全件のデータが取得できるようになります。
脆弱性の原因
リテラルに原因があります。
SQLの標準機能で文字列はシングルクォーテーションで囲むことになっています。
入力値にシングルクォーテーションが含まれる場合、入力値をそのまま使いSQL文を組み立てることで予期しないSQLを作ることができます。
例えば、入力値を基に商品を検索する機能があるとします。
入力値が「靴」と入力された場合に以下のようなSQLが呼び出せれます。
SELECT * FROM item WHERE category = '靴';
不正な入力値' OR '1'='1
を入力した場合は以下のSQLが呼び出されます。
SELECT * FROM item WHERE category = '' OR '1'='1'
シングルクォーテーションを重ねることにより、文字列リテラルが終了し、後続に任意の処理を追加することができます。
SQLの実装すると、SQLインジェクションが成立します。
name
に' OR '1'='1
が渡されると、変数queryは SELECT * FROM User WHERE name =' OR '1'='1'
という文字列になります。
queryを実行するため不正なSQLが実行されることになります。
public List<User> selectUsersByName(String name) {
String query = String.format("SELECT * FROM User WHERE name = '%s'", name);
RowMapper<User> rowMapper = new UserRowMapper();
return this.jdbcTemplate.query(query, rowMapper);
}
対策方法
- エスケープ処理を行う
- プレースホルダを利用する
エスケープ処理は実装漏れなどの可能性があり、完全な対応が難しい為、プレースホルダを利用することが推奨されています。
プレースホルダによる組み立て
可変な値(パラメータ部)に?
を使ってSQL文に埋め込むやり方です。
SpringBoot(jdbcTemplateを利用)の場合は、以下のように書くことができます。
public List<User> selectUsersByName(String name) {
String sql = "SELECT * FROM user WHERE name = ?";
RowMapper<User> rowMapper = new UserRowMapper();
return this.jdbcTemplate.query(sql, rowMapper, name);
}
プレースホルダによる組み立ては、以下の2種類があります。
2つの違いとしてはバインド(パラメータに値を埋め込むこと)するタイミングが異なります。
静的プレースホルダ
プレースホルダのままSQLをDBエンジンに渡し、SQLを実行する時にパラメータの値をDBに渡します。
バインド処理はDBエンジンで行う為、安全とされています。
動的プレースホルダ
バインド処理をアプリケーション側で行います。
ライブラリでエスケープ処理が行われるので、自前でエスケープ処理を行うよりは安全とされていますが、ライブラリによっては、SQL構文を変化させてしまう可能性があるため静的プレースホルダが推奨されています。