SQLインジェクションとは
SQLインジェクション(SQLi)は、悪意のあるユーザーがWebアプリケーションにSQL文を注入することで、データベースを不正に操作する攻撃手法です。
たとえば、ログインフォームに入力された値をそのままSQLに組み込むと、次のような脆弱性が生じます。
-- 脆弱な例
SELECT * FROM users WHERE username = '$inputUsername' AND password = '$inputPassword';
このとき、もし$inputUsernameに' OR '1'='1を入力されると、SQL文は次のようになります。
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '';
'1'='1'は常に真なので、誰でもログインできてしまいます。
主なSQLインジェクションの種類
1. ブラインドSQLインジェクション(Blind SQLi)
攻撃結果が画面に表示されない場合でも、挙動から内部の状態を推測するタイプの攻撃です。
-- 例:IDの存在確認
SELECT * FROM products WHERE id = 10 AND 1=1;
SELECT * FROM products WHERE id = 10 AND 1=2;
表示の有無で「条件が通ったか」を知ることができます。
2. エラーベースSQLインジェクション(Error-based SQLi)
意図的にエラーを発生させ、そこから情報を引き出す攻撃です。
-- 例:意図的なエラーを出す
SELECT * FROM users WHERE id = 1 UNION SELECT 1, @@version, 3;
DBのバージョンなどが画面に表示される場合があります。
3. UNIONベースSQLインジェクション
UNIONを使って、本来のクエリと別の結果を結合して表示させる攻撃です。
-- 例:usersテーブルと攻撃者のクエリを結合
SELECT name, email FROM users WHERE id = 1
UNION SELECT username, password FROM admins;
SQLインジェクションの対策
1. プレースホルダを使う(バインド変数)
// Kotlin + JDBCの例
val sql = "SELECT * FROM users WHERE username = ? AND password = ?"
val ps = connection.prepareStatement(sql)
ps.setString(1, inputUsername)
ps.setString(2, inputPassword)
val rs = ps.executeQuery()
→ プレースホルダを使うことで、ユーザー入力がSQL構文と分離される。
2. ORM(Object-Relational Mapping)の使用
// JPAやMyBatisなどのORMフレームワークを使う
userRepository.findByUsernameAndPassword(username, password)
→ ほとんどのORMは自動でプレースホルダを使うため、問題ない。
ただしチーム内できちんとORMを使うようコーディング規約は設けておくこと。
大抵はこれで対策済ではある。
3. 入力バリデーション・サニタイズ
// IDや数字を受け取るときは数値チェックを行う
require(id.matches(Regex("\\d+"))) { "Invalid ID" }
→ SQLではなく入力側で「受け付けてよい値」だけを許可することで、リスクを軽減できますが大抵は1, 2で対策済みで不要な場合が多い。
4. データベースの権限設定
-- 最小限の権限だけ与える
CREATE USER 'webuser'@'localhost' IDENTIFIED BY 'password';
GRANT SELECT, INSERT ON mydb.* TO 'webuser'@'localhost';
→ 万が一攻撃されても、影響範囲を限定できる。
参考サイト
追記
この記事はAIで添削しています。