SQLインジェクション
SQL Injection
Webアプリケーションの脆弱性を突くサイバー攻撃の一つ。ユーザーの入力欄にデータベースを操作するための不正な命令(SQL文)を紛れ込ませ、データの漏洩や改ざん、削除を意図的に引き起こす非常に危険な手法です。
🐾 猫で例えると?
給餌器のそばで、受け皿に顔を向ける茶トラ。ここで、注文を書いて渡す入口に「ごはんを一つ」という値だけでなく、機械の設定を変える命令まで入れられると想像してみましょう。SQLインジェクションでは、入力を値として扱う境界が崩れ、SQLの命令の一部として解釈されることが問題です。入力をSQLの構文へつなげず、値として渡す設計が必要です。
💻 IT現場における「SQLインジェクション」とは?
プログラミングの現場において、SQLインジェクション対策は「絶対に避けては通れない基本中の基本」です。検索フォームやログイン画面など、ユーザーが自由に入力できる箇所はすべて攻撃の対象になり得ます。
対策が不十分な場合、本来見せてはいけないデータの取得や、許可していない変更・削除につながるおそれがあります。影響する範囲はSQLの組み立て方とDB接続権限によって変わるため、入力の見た目だけで安全と判断せず、命令と値を分けて扱います。
⚠️ SQLインジェクションの仕組みと注意点
ユーザー入力をSQLへ連結すると、引用符などで入力欄の値がSQLの条件式に解釈される場合があります。結果として本来返すべきでない行を読めたり、認証条件を変えられたりします。具体的な影響はSQLの構造とDB接続ユーザーの権限によって異なります。
プレースホルダー(バインド機構)を使った防御
値を渡す箇所では、命令文の形を先に用意するプリペアドステートメントを使います。値を入れる指定箇所(プレースホルダー)へデータを別に渡し、命令と値を分離します。次はPHPからデータベースへ接続するPDOと、usersテーブルが用意され、usernameが文字列として検証済みという前提の検索例です。ログイン機能全体の実装ではありません。
// PHP (PDO) での安全な実装例
// 危険な例(絶対ダメ!):入力値をそのまま結合している
// $sql = "SELECT * FROM users WHERE name = '" . $_POST['username'] . "'";
// 安全な例:プレースホルダー(:name)を使用する
$sql = "SELECT * FROM users WHERE name = :name";
$stmt = $pdo->prepare($sql);
// 入力値は「単なるデータ」としてバインドされるため、不正なSQLコマンドは実行されない
$stmt->bindValue(':name', $_POST['username'], PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll(); 値はパラメーターで渡せますが、テーブル名や並べ替えに使う列名を同じ方法で指定できるとは限りません。例えば並び順を選ばせる場合は、あらかじめ用意した列名の許可リストから選び、入力を直接SQLへ連結しないようにします。
🛠️ SQLインジェクションを防ぐためのポイント
現場でセキュアなシステムを構築するための基本的な防御策です。どれか一つではなく、複数の対策を組み合わせる多層防御が重要です。
- プリペアドステートメントの徹底: SQL文を動的に生成する際は、文字列連結を絶対に避け、フレームワークが提供するプレースホルダーやORM(Object-Relational Mapping)を正しく利用します。
- 入力値のバリデーション(検証): そもそもシステムが想定していない形式のデータ(文字数、文字種など)が入力された時点で、データベースへ問い合わせる前にエラーとして弾く仕組みを作ります。
- Webへの攻撃を検知・遮断するWAFは補助として利用する: 攻撃パターンの検知は役立ちますが、アプリの安全なクエリ構築の代わりにはなりません。DB接続権限も必要な操作に絞ります。
給餌器へ手を差し込む茶トラの例えでは、受け取り口から内部の動作へ干渉しようとする点がポイントです。SQLでは入力値が命令に混ざることを防ぎます。値を文字列連結で埋め込まず、プレースホルダーへ渡す形になっているかを確認しましょう。入力チェックやWAFを追加しても、SQLの組み立て方そのものの修正は必要です。