~/VibeHandbook
無料 PDF

18 · 02

インジェクション: 入力がコードになるとき

インジェクションは最も古く、いまだに最もありふれた深刻なバグだ。ユーザー由来のデータが、データではなく命令として扱われたときに起こる。ほとんどのアプリにとって重要なのは2種類だ。

injection ——SQL(ストラクチャード・クエリ・ランゲージ。アプリがデータベースにデータを問い合わせるために使う言語だ)——は、ユーザー入力がデータベースクエリに紛れ込むことだ。典型的な脆弱パターンはこうだ。

// VULNERABLE: ユーザーの入力がそのままクエリに貼り付けられている
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query(`SELECT * FROM users WHERE name = '${name}'`);
});
// もし誰かが  name = '; DROP TABLE users; --  を渡すと
// クエリはテーブルを削除するコマンドになってしまう。

修正方法はパラメータ化クエリ(プリペアドステートメントとも呼ばれる)だ。クエリとデータを別々に送るので、データベースが一方を他方と取り違えることがない。

// SAFE: 値はパラメータとして渡され、決してコードとして扱われない
app.get("/user", (req, res) => {
  const name = req.query.name;
  db.query("SELECT * FROM users WHERE name = ?", [name]);
});

ルールはこうだ。クエリを文字列連結で組み立ててはいけない。 diffの中で、変数の周りにSQLを組み立てるバッククォートや + を見かけたら、手を止めてパラメータ化版を要求すること。

二つの経路は同じユーザー入力をデータベースに入れますが、データベースがその入力をコマンドと取り違えないようにするのは、片方だけです。

  ユーザー入力:'; DROP TABLE users; --
        │
        ├─────────────────────────┬───────────────────────────┐
        ▼                         ▼                            
  ✗ 文字列連結              ✓ パラメータ化                     
  "...WHERE name=             "...WHERE name = ?", [input]      
     '" + input + "'"          (クエリとデータを分離送信)      
        │                         │
        ▼                         ▼
  ┌───────────────┐         ┌───────────────┐
  │ データベース  │         │ データベース  │
  │入力を命令とし │         │入力をデータと │
  │て読む         │         │してのみ扱う   │
  │ → テーブル消失│         │ → 安全な照会  │ 
  └───────────────┘         └───────────────┘
       侵害                      安全

XSS (cross-site scripting) は、ブラウザにおける同じ発想だ。ユーザー入力を受け取って、それを生のHTML(ハイパーテキスト・マークアップ・ランゲージ。ブラウザに何を表示するかを伝えるコードだ)としてページに落とし込むと、攻撃者は <script> タグを注入でき、それがあなたの他のユーザーのブラウザで実行される——たとえばセッションを盗むために。修正方法はエスケープだ。ユーザーのコンテンツをHTMLではなくテキストとしてレンダリングする。Reactのようなモダンなフレームワークはデフォルトでエスケープしてくれる。これは素晴らしいことだが、AIが「動くようにする」ために dangerouslySetInnerHTMLinnerHTML のような抜け道に手を伸ばすまでの話だ。それらは保護をバイパスする。diffの中でそうした呼び出しを見かけたら、疑うべきものとして扱うこと。

あらゆるインジェクションバグの背後にある統一原理はこうだ。データとコードを分離し続けること。 ユーザー入力がクエリ・コマンド・テンプレート・HTMLに渡るときは必ず、何かがそれをエスケープするかパラメータ化しなければならない。

オフラインでも読みたい?

本編まるごとを PDF または EPUB で無料ダウンロード。