PR

SQLインジェクション(SQL Injection)について

この記事は学習用です。ChatGPT と GitHub Copilot を使用しています。

1. 導入:定義とリスク

SQLインジェクション(SQLi)は、攻撃者がアプリケーションの入力欄に悪意ある文字列を入れることで、アプリ側が生成する SQL 文の構造を変化させ、想定外のデータ取得・改変・削除、あるいは認証回避などを行う攻撃手法です。これは Web アプリケーションに対する代表的なインジェクション攻撃の一種で、データ漏えい・完全な DB 侵害・ランサムウェアや身代金要求につながる重大なリスクを伴います。(PortSwigger

なぜ重要か:

  • 攻撃の影響範囲が大きい(全テーブル抽出や権限昇格が可能)。(PortSwigger
  • 自動化ツール(sqlmap など)で短時間に検出・悪用されやすい。(ウィキペディア
  • 防御方法は確立されているものの、実装ミスや例外処理漏れで残存しやすい。(OWASP Cheat Sheet Series

2. 種類と具体例

主要な SQLi の種類を定義し、実際的なペイロード例と共に示します。

2.1 種類(定義)

  • エラー型(Error-based SQLi):DB のエラーメッセージを利用して情報を得ます。
  • ブラインド型(Blind SQLi):エラーを出さない環境で、真偽応答や時間差で情報を引き出します(Boolean-based / Time-based)。(OWASP
  • UNION ベース型(Union-based SQLi):UNION SELECT を使って任意の結果セットを結合し、データ抽出を行います。
  • 認証バイパス型:ログインフォームに ' OR '1'='1 などを入れて認証を回避します。(PortSwigger

2.2 具体的なペイロード例(MySQL 向け)

目的パラメータ例(入力)説明
認証バイパス' OR '1'='1WHERE 条件を常に真にする
列名・環境情報取得(UNION)' UNION SELECT user(),database(),version() --環境情報取得
ブラインド(時間差)' OR IF((SELECT COUNT(*) FROM users WHERE username='admin' AND SUBSTRING(password,1,1)='a'), SLEEP(5), 0) --特定文字の存在でレスポンスタイムを遅延させる

(実際に試す場合は、必ず許可された学習環境でのみ実施してください。無断の攻撃行為は違法となる可能性があります。)

3. 発生原因(根本原因とコーディングパターン)

実務でよく見られる原因と、典型コード例(脆弱な例と安全な例)を示します。

3.1 根本原因(要点)

  1. 動的 SQL の文字列連結(ユーザー入力を直接埋め込む)。
  2. 入力の型付けが不十分(数値パラメータを文字列として扱う等)。
  3. エラーメッセージを露出(DB エラー内容をそのまま返す)。
  4. 過剰な DB 権限(アプリが不要な DDL/DCL 権限を持っている)。(CISA

3.2 コード例(脆弱 → 改修)

脆弱な PHP(文字列連結)

// 脆弱例(絶対に使わない)
$username = $_GET['user'];
$sql = "SELECT * FROM users WHERE username = '$username'";
$result = mysqli_query($conn, $sql);

改修例:PDO のプリペアドステートメント(推奨)

// 安全例(プリペアドステートメント)
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = :username");
$stmt->execute([':username' => $_GET['user']]);
$rows = $stmt->fetchAll();

PDO やパラメータ化クエリを正しく使うことで、入力が SQL 構造に影響しないよう分離(バインド)できます。ただし実装ミスや拡張層の設定(例:PDO のエミュレート・プリペアドステートメント ATTR_EMULATE_PREPARES)による挙動差もあり得るため、ライブラリと設定の安全な使い方を理解しておきましょう。(Stack OverflowPHP マニュアル

4. 検出・テスト手順

脆弱性を見つけるための手順とツール、注意点を具体的に示します。

4.1 手順(ホワイトボックス / ブラックボックス)

  1. 入力ポイントの列挙:GET/POST/Headers/Cookies/JSON ボディ/ORM 経由のクエリを洗い出します。
  2. 簡易ペイロードで反応確認:' OR '1'='1' TEST' などでレスポンス変化やエラーを観察します。
  3. エラー解析:返却されるエラーメッセージの内容をログで確認します(本番でのエラー表示は不可)。(CISA
  4. 自動ツールで網羅検査:Burp Suite、sqlmap、OWASP ZAP などで自動診断します。Burp には SQLi 検査ワークフローとスキャナーがあります。(PortSwigger
  5. ブラインド攻撃の検証:Boolean・Time-based 手法でデータ漏えいの可否を確認します(アプリや DB への負荷に注意)。(OWASP

4.2 ツールとコマンド例

  • sqlmap:sqlmap -u "http://example.com/item?id=1" --batch --dump(危険 — 許可済み環境のみ)。(ウィキペディア
  • Burp Suite:Proxy でリクエストをキャプチャ → Active Scan / Intruder で投入。(PortSwigger

4.3 レポーティング時の重要項目

  • 脆弱なエンドポイント、再現手順、悪用で得られる影響(機密度合い)、推奨対策(コード修正+運用)を具体的に提示します。

5. 実務的対策(設計・実装・運用)

最優先ルール:入力データは「絶対に」SQL 構文として解釈されないよう分離します(パラメータ化)。その上で多層防御を実装します。

5.1 優先度の高い対策(実装手順)

  1. パラメータ化クエリ/プリペアドステートメントの徹底(最も効果的)。例:PDO(PHP)、psycopg2(Python)、pg(Node.js)。(OWASP Cheat Sheet Series
  2. ORM のクエリバインディングを正しく利用する(生 SQL 発行箇所はレビューする)。
  3. 入力の正当性検査(ホワイトリスト):型・長さ・正規表現による厳密な検証。
  4. 最小権限の DB ユーザー:データ取得のみのアプリは読み取り専用権限で接続する。(CISA
  5. エラーメッセージの非表示・マスキング:詳細な DB エラーをクライアントへ返さない。
  6. ログと検知ルール(WAF/ログ監視):異常なクエリパターンでアラート。ただし WAF だけに頼らない。最新研究では WAF 回避テクニックが報告されています。(arXiv

5.2 コード例(Python psycopg2)

# 安全:バインドパラメータを使用
cur.execute("SELECT id, name FROM users WHERE email = %s", (email,))

5.3 実運用での注意点

  • レガシー SQL や動的オブジェクト名(テーブル名・列名をユーザー入力で決める)は危険です。やむを得ず必要な場合は強いホワイトリストで制限します。
  • プリペアドステートメントでも落とし穴があります。例として、PDO のエミュレート設定による挙動差などがあるため、ドライバーや設定を理解しておきましょう。(PHP マニュアル

6. 実践例:簡易演習(SQLite ローカルで安全に試す)

学習用にローカルの SQLite DB で脆弱性を再現・修正する手順を示します(必ず学習用環境で)。

6.1 環境(例)

  • Python 3.x、SQLite(組み込み)
  • サンプルスクリプト vuln_demo.pyfixed_demo.py

vuln_demo.py(脆弱)

import sqlite3, sys
conn = sqlite3.connect('demo.db')
c = conn.cursor()
user = sys.argv[1]  # 危険!外部入力を直接使う
query = "SELECT * FROM users WHERE username = '%s'" % user
print("QUERY:", query)
for row in c.execute(query):
    print(row)

実行例(脆弱動作の確認):

python vuln_demo.py "admin' OR '1'='1"

fixed_demo.py(対策)

import sqlite3, sys
conn = sqlite3.connect('demo.db')
c = conn.cursor()
user = sys.argv[1]
query = "SELECT * FROM users WHERE username = ?"
for row in c.execute(query, (user,)):
    print(row)

(学習時は demo.db を事前に作成・投入しておいてください。実運用 DB では絶対に行わないでください。)

7. よくある誤り・落とし穴(監査とコードレビュー)

  • 部分的なサニタイズで満足する:エスケープだけでは抜け道が残る場合があります。
  • フレームワークのデフォルトを過信する:ORM の raw SQL や例外処理で簡単に穴が開きます。
  • デバッグ用にエラーを出しっぱなし:公開環境での詳細エラーは情報供与になり得ます。
  • WAF に全てを任せる:WAF は有用ですが回避技術も存在するため、アプリ側防御が最優先です。(arXiv

8. まとめ・チェックリスト

8.1 主要ポイント

  • SQL インジェクションは予防可能な脆弱性であり、パラメータ化クエリ+入力のホワイトリスト+最小権限が基本対策です。(OWASP Cheat Sheet Series

8.2 導入チェックリスト(実務で確認する項目)

  • [ ] アプリの全ての SQL 発行箇所がレビュー済みか。
  • [ ] パラメータ化クエリ(プリペアドステートメント)が使われているか。
  • [ ] ORM の raw SQL 使用箇所を特定し監査したか。
  • [ ] DB ユーザー権限は最小化されているか。
  • [ ] 本番で詳細な DB エラーメッセージを返していないか。
  • [ ] 自動スキャン(Burp/sqlmap など)と手動ペネトレーションを定期実施しているか。(PortSwigger

A. 参考サイト

以下は信頼できる公式資料や実践チュートリアルです。社内共有資料や教育資料に使う際は出典を明記してください。

  1. OWASP — SQL Injection Prevention Cheat Sheet.
  2. PortSwigger — What is SQL Injection?(Web Security Academy)
  3. NIST — Glossary / SQL Injection 定義
  4. CISA — Practical SQLi Identification(実務ガイド)
  5. Burp Suite ドキュメント(SQLi テストワークフロー)

B. 関連書籍

C. 注意

サンプルや手順は学習目的で提供します。第三者のシステムで無断にテストや攻撃を行うと違法行為となります。必ず許可を得た環境(社内のステージング環境・ラボ)でのみ実行してください。

コメント