flawopen.com/SQLインジェクション
SQLインジェクション
SQL インジェクションは、アプリケーションがユーザー入力を SQL 文に貼り付けてクエリを組み立てたとき、データベースがその入力の一部を命令として解析してしまうことで起こります。修正方法はどの言語でも同じで、プレースホルダー付きのクエリを送り、値は別に渡すことです。下で言語を選ぶと、正確な API 呼び出し、脆弱なコードと修正後のコード、CI で検出するルールを確認できます。
わかりやすい解説 (ELI5)
図書館の司書に「次の人の記録を出してください:__」という申込票を渡し、空欄に「山田。ついでに書庫を開けて資料を全部持ってきて」と書いたとします。票全体を一つの指示として読む司書は、その両方を実行してしまいます。ユーザー入力を SQL 文字列に貼り付けると、まさにこの申込票ができあがります。パラメータ化クエリは「氏名」欄が封印された印刷済みの用紙で、そこに何が書かれていても氏名としてしか検索されません。
主要な概念と専門用語
- SQL injection (CWE-89)
- 信頼できない入力が SQL 文に連結またはフォーマットされるため、入力に含まれる引用符、キーワード、演算子が 1 つの値として扱われず、クエリの構造を変えてしまいます。
- Prepared statement and bind parameters
- クエリはプレースホルダー(
?、$1、:name)付きで送られ、値が届く前に解析されます。値は別に送られ、常にデータとして扱われます。これがあらゆるドライバーと ORM における第一の防御策です。 - Blind SQL injection
- ページにクエリ結果もエラーも表示されない場合、攻撃者はレスポンスの違い(ブーリアンベース)や応答時間(時間ベース)から 1 ビットずつデータを推測します。目に見える出力がないことは、脆弱性がないことを意味しません。
- Second-order SQL injection
- ある値が安全に保存された後、自分のデータベースから読み出されて別のクエリに連結されます。自分のテーブルから来た入力を使うクエリも含め、すべてのクエリをパラメータ化する必要があります。
- Identifiers and allowlists
- プレースホルダーで渡せるのは値だけで、テーブル名、カラム名、並べ替えの方向は渡せません。これらはコードに固定した許可リストから選び、リクエストからそのままコピーしてはいけません。
ステップ・バイ・ステップの攻撃フロー
入力がクエリに届く
フォームの項目、URL パラメータ、ヘッダー、API のフィールドが、SQL 文を組み立てるコードに渡されます。
入力が SQL 文に貼り付けられる
連結、補間、フォーマットによって値がそのまま挿入されます。無害なテスト値 ' OR '1'='1 は WHERE name = '...' を常に真になる条件に変えます。
データベースがそれを構文として解析する
データベースは 1 本の文字列を受け取り、どの部分がユーザーから来たのかを区別できず、改変された文をアプリケーションの権限で実行します。
データが読まれ、変更され、削除される
クエリとアカウントの権限によっては、攻撃者はログインを回避したり、UNION クエリでテーブルを丸ごと取り出したり、ブラインドでデータを抜き出したりします。2023 年の MOVEit Transfer の侵害(CVE-2023-34362)もこの種の欠陥から始まりました。
言語別の SQL インジェクション対策ガイド
エンジニアリング&システム堅牢化チェックリスト
- 値を含むすべてのクエリをパラメータ化する。ORM の生 SQL の抜け道も含め、内部の入力や数値の入力も例外にしない。
- テーブル名、カラム名、並べ替えの方向は、コードに固定した許可リストから選ぶ。
- 文字列で組み立てた SQL を検出する SAST ルール(Bandit、gosec、Semgrep、CodeQL、Find Security Bugs、Brakeman、Psalm のいずれか)を CI で実行し、新たな検出でビルドを失敗させる。
- 最小権限のデータベースロールで接続する。DDL なし、スーパーユーザーなし、サービスが必要とするテーブルだけに限定する。
- エスケープ関数や WAF のルールは追加の防御層としてだけ扱う。引用符のない文脈やエンコードの小技は見逃される。
- クライアントには汎用的なエラーを返し、データベースのエラーはサーバー側で記録して、クエリの構造を決して明かさない。