SQLインジェクション
SQL Injection
概要(サマリー)
SQLインジェクションとは、Webサイトの入力フォームなどに悪意のある文字を入力することで、Webアプリの裏側で動いているデータベース操作言語(SQL)の命令を意図しない形に書き換え、データベースを不正に操作するサイバー攻撃手法である。
例えば、荷物の受取サインをする書類に「サインした人の借金をすべて帳消しにする」という余計な指示を紛れ込ませてサインさせ、本来の目的とは違う処理を実行させてしまうようなものである。これにより、顧客情報の漏洩やデータの改ざん、最悪の場合はデータベース全体の削除といった深刻な被害をもたらす。
詳細解説
データベースへの「侵入命令」
Webアプリケーションの多くは、ユーザーが入力したキーワードなどをもとにSQL文を組み立てて、データベースに問い合わせを行う。SQLインジェクション(Injection=注入)は、その組み立ての隙を狙って、開発者が想定していなかった「不正なSQL命令」を注入する攻撃である。
SQLインジェクションが発生する仕組み
この脆弱性は、ユーザーの入力をそのままSQL文の文字列と結合して実行してしまうことで発生する。
悪い例(脆弱性のある実装)
// ユーザーが入力した username(例: admin)をそのまま結合
$query = "SELECT * FROM users WHERE username = '" . $_POST['username'] . "' AND password = '" . $_POST['password'] . "'";
もし攻撃者が、username の入力欄に ' OR '1'='1 という文字列を入力した場合、組み立てられるSQL文は以下のようになる。
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '...'
SQLにおいて '1'='1' は常に真(正しい)となるため、パスワードの検証が無視され、攻撃者は他人のアカウント(あるいは管理者アカウント)で強制的にログインできてしまう。
具体的な被害のシナリオ
SQLインジェクションが成功すると、以下のような深刻な被害が発生する。
- 個人情報・秘密情報の漏洩: データベース内のすべての顧客データ、クレジットカード情報、パスワードなどが盗み出される。
- データの改ざん・消去: データベース内の情報を書き換えられたり、テーブルそのものを削除(
DROP TABLE)されたりする。 - 認証のバイパス: パスワードを知らなくても、管理者権限でシステムにログインされてしまう。
プレースホルダ(プレパードステートメント)による対策
SQLインジェクションを防ぐための最も効果的かつ根本的な対策は、プレースホルダ(プレパードステートメント)を使用することである。
プレースホルダとは、SQL文のテンプレートを先にデータベース側に送信して解析させておき、後からユーザーの入力値(パラメータ)を安全にはめ込む仕組みである。
良い例(プレースホルダを使用した安全な実装)
// SQL文の骨組みを先に定義(? がプレースホルダ)
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ? AND password = ?');
// 値を安全にバインドして実行
$stmt->execute([$_POST['username'], $_POST['password']]);
この方法であれば、たとえ入力値に ' OR '1'='1 のような悪意のある文字列が含まれていても、データベースはそれを単なる「文字データ(ユーザー名)」として扱い、SQLの命令文の一部としては解釈しないため、攻撃は完全に無効化される。
入力値の適切なバリデーション
補助的な対策として、入力フォームに値が送信されてきた段階で、期待される形式(数値のみ、メールアドレス形式のみなど)であるかを厳密にチェックするバリデーションも重要である。これにより、不正な文字列がプログラムの奥深くまで入り込むのを手前で防ぐことができる。
AIコーディングとの関係
AIは非常に高速にコードを生成してくれるが、プロンプトでセキュリティへの配慮を指示しない場合、結合演算子を使った脆弱な生SQLコードを出力することがある。
特に「手軽に動くシンプルなデータベース接続コードを書いて」とAIに依頼すると、対策が省略されたコード(上記の「悪い例」のようなコード)が生成されやすい。
AIコーディング時のポイント
- SQLを扱うコードを生成させる際は、必ず「プレースホルダを使用し、SQLインジェクション対策を施した安全なコードにしてください」と言明する。
- AIにコードをレビューしてもらう際、「このコードにSQLインジェクションをはじめとするセキュリティ上の脆弱性は含まれていませんか?」と問いかけることで、見落としていた隙を発見させることができる。
よくある勘違い
特殊な文字(シングルクォート等)だけを禁止すれば十分?
「入力欄に '(シングルクォート)や ;(セミコロン)を入力できないように制限(エスケープ処理)すれば大丈夫」と考えるのは危険である。
エスケープ漏れが発生するリスクがあるほか、数値型のパラメータ(例:id = 1)を扱うSQL文では、シングルクォートを使わなくてもSQLインジェクションが成立する場合がある。文字の禁止ではなく、プレースホルダを徹底することが根本解決となる。
フレームワーク(ORM)を使っていれば絶対に安全?
LaravelなどのモダンなフレームワークやORM(Object-Relational Mapping)は、内部で自動的にプレースホルダを使用しているため、基本的には安全である。
しかし、開発者がクエリの自由度を高めるために「生SQL(Raw Query)」を記述するメソッド(例:Laravelの whereRaw() や selectRaw())を使い、そこにユーザー入力を直接結合してしまうと、依然としてSQLインジェクションの脆弱性が発生する。
静的サイトやフロントエンドだけのアプリでも標的になる?
データベースと直接通信しないHTML/CSSだけの静的サイトであれば、SQLインジェクションの心配はない。
ただし、フロントエンド(JavaScript)からAPIを呼び出している場合、そのAPI(バックエンド)の先でデータベースが動いていれば、APIリクエストを通じて攻撃が行われる。保護すべきなのはクライアント側ではなく、常にデータを処理するサーバー(データベース)側である。
まとめ
- SQLインジェクションは、入力フォームなどから悪意のあるSQL命令を「注入」してデータベースを不正操作する攻撃。
- ユーザー入力をそのままSQL文の文字列と結合してしまうことで脆弱性が発生する。
- 根本的な対策は、SQLの命令とデータを分離して処理する「プレースホルダ(プレパードステートメント)」を使用すること。
- ORMやフレームワークを使っていても、生SQL(Raw)を直接書く場合は脆弱性が作り込まれやすいので注意する。
- AIにSQLクエリを含むコードを生成させるときは、必ずセキュリティ対策を施すよう明示的に指示する。
情報ソース
より詳しくAIに聞いてみよう
- SQLインジェクションの仕組みを、郵便物の宛名書きに余計な指示を書き足す比喩を使って説明してください。
- プレパードステートメント(プレースホルダ)が、なぜSQLインジェクションを完全に防げるのかを技術的に詳しく教えてください。
- PHPのPDO、またはJavaScript(Node.js)のライブラリを使って、安全にデータベースと通信する具体的なコード例を教えてください。
- AIにデータベース操作を行うAPIコードを生成させるとき、セキュリティ脆弱性を防ぐための「指示テンプレート」を作ってください。
- 自分のソースコードをAIに見せて「SQLインジェクションの脆弱性がないか静的解析してください」と頼む場合の、効果的なレビュー用プロンプトを教えてください。