~/VibeHandbook
$39

18 · 02

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

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

SQL injection は、ユーザー入力がデータベースクエリに紛れ込むことだ。典型的な脆弱パターンはこうだ。

// 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を組み立てるバッククォートや + を見かけたら、手を止めてパラメータ化版を要求すること。

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

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

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

PDF + EPUB + ダウンロード可能なプロンプトライブラリ + バージョンアップデートを入手しよう。

$ PDFを入手 — $39