「バリデーションだけで防げる」という幻想を捨てろ。インジェクション攻撃を根絶する防壁の作り方
現場でコードレビューをしていると、未だに後を絶たないのが「文字列結合によるクエリ生成」だ。
「入力値はフロントエンドでバリデーションしているから大丈夫」「特殊文字はエスケープしているから問題ない」――そう信じているなら、その認識は今日この瞬間に捨ててほしい。攻撃者は君たちが作った「甘い穴」を、笑いながら通り抜けていく。
今日は、SQLインジェクションを筆頭としたインジェクション攻撃の息の根を止めるための、泥臭くも確実な防衛戦略を共有しよう。
—
1. なぜ「文字列結合」が死を招くのか(PoCの視点)
攻撃者が狙っているのは、開発者が意図した「命令」と、入力された「データ」の境界線だ。
例えば、ユーザーIDで検索するだけの何気ないコードを想像してほしい。
— 開発者の意図
SELECT FROM users WHERE id = ‘ユーザー入力値’;
ここで攻撃者が 1' OR '1'='1 という文字列を入力したらどうなる?
クエリは SELECT FROM users WHERE id = '1' OR '1'='1'; に書き換わり、全ユーザーの個人情報が画面にさらされる。これがSQLインジェクションの基本だ。
「特殊文字をエスケープすればいいのでは?」と考えるかもしれないが、文字コードの不一致やデータベース特有の挙動を突けば、エスケープの壁は容易に突破される。「データをコードとして解釈させない」こと以外に、安全な道はない。
—
2. プリペアードステートメントの強制(実装サンプル)
プリペアードステートメントの真髄は、「命令(SQL)」と「データ(パラメータ)」を物理的に分離することにある。データベースサーバー側で先にSQLのひな形をコンパイルし、後からデータを流し込む。これなら、データにどんな悪意ある命令が混入していても、ただの「文字列」としてしか扱われない。
PHP (PDO) での実装
MySQLiではなく、必ずPDOを使え。
// 悪い例:文字列結合(絶対にやるな)
// $db->query(“SELECT FROM users WHERE id = ” . $_GET[‘id’]);
// 正しい例:プリペアードステートメント
$stmt = $pdo->prepare(“SELECT FROM users WHERE id = :id”);
// 実行時にパラメータをバインドする
$stmt->execute([‘id’ => $_GET[‘id’]]);
$user = $stmt->fetch();
Python (psycopg2 / SQLAlchemy) での実装
Pythonエンジニアも、f-stringや+を使ったSQL構築は厳禁だ。
PostgreSQL向けの例
プレースホルダは %s を使う(ライブラリが適切にエスケープする)
cursor.execute(“SELECT FROM users WHERE id = %s”, (user_id,))
JavaScript (Node.js / mysql2) での実装
Node.js環境でも同様だ。
// mysql2ライブラリを使用
const sql = ‘SELECT FROM users WHERE id = ?’;
connection.execute(sql, [userId], (err, results) => {
// 安全に結果を取得
});
—
3. ORMとクエリビルダの罠
「ORMを使っているから大丈夫」というのも危険な思い込みだ。多くのORMやクエリビルダは安全だが、「Rawクエリを実行するメソッド」を呼び出した瞬間に、君たちは自ら防壁を破壊することになる。
- ルール: 可能な限りORMのメソッド(
where(),find(),save()等)を使い、生のSQLを直接書く機能は、セキュリティ審査を通過しない限り封印せよ。 - 動的クエリ生成の禁止: テーブル名やカラム名をユーザー入力から動的に生成するのは論外だ。どうしても必要な場合は、必ず「ホワイトリスト(許可された値のリスト)」と照合し、それ以外を拒否するロジックを挟め。
—
4. 運用の要:WAFと多層防御
アプリケーションコードの修正が最優先だが、インフラ側での防御も怠るな。
Nginx / WAFの設定指針
万が一、アプリケーションに脆弱性が残っていた場合の「最後の防波堤」として、AWS WAFやModSecurity(OWASP Core Rule Set)を導入しておくことを強く推奨する。
AWS WAFのSQLインジェクション保護設定の考え方:
SQLiマッチングルールを有効化する。- 異常なクエリパターン(
UNION SELECT,SLEEP(),information_schema等)を検知し、即座にブロック(403 Forbidden)させる設定を入れる。
ただし、WAFはあくまで補完だ。WAFがあるからといって、アプリケーション側の脆弱性を放置する言い訳にはならない。
—
最後に:エンジニアとしての矜持
インジェクション攻撃を防ぐことは、単なる「バグ修正」ではなく、「ユーザーの人生を守る」という我々エンジニアの責務だ。
今日から以下のルールをチームに徹底してほしい。
1. 文字列結合によるSQL構築をコードベースから排除する。
2. プリペアードステートメント以外の利用を禁止する。
3. ORMのRaw実行メソッドを使用する際は、必ずセキュリティレビューを通す。
「動けばいい」コードは、明日には負債になり、明後日にはインシデントの引き金になる。堅牢な設計こそが、エンジニアとしての君たちの価値を最大化するはずだ。
何か不安な実装があれば、いつでも相談してくれ。技術の力で、安全なサイバー空間を作り上げよう。
コメント