玄関の鍵を「壊さず」開けさせるな!インジェクション攻撃からアプリを守る鉄則
こんにちは。現場でセキュリティの泥臭い対応を長年続けてきた者です。
今日は「インジェクション攻撃」についてお話しします。名前は難しそうですが、仕組みはとてもシンプル。そして、これを知っているだけであなたの書くコードの安全性は劇的に上がります。
さっそく、皆さんの身の回りの「家の防犯」に例えて解説していきましょう。
—
1. インジェクション攻撃って、結局なに?
想像してください。あなたは自分の家の玄関に「宅配便のメモ」を貼りました。そこには「不在なので玄関前のボックスに入れておいてください」と書いてあります。
正常な配達員さんは、そのメモを読んでボックスに荷物を入れますよね。これが正常なプログラムの動きです。
ところが、もし「悪意のある泥棒」がやってきて、そのメモを勝手に書き換えたらどうなるでしょう?
「不在なので、玄関の鍵を開けて中に入れておいてください」と。
配達員さん(プログラム)は、メモの内容を疑わずにドアを開けてしまいます。これが「インジェクション(注入)」です。攻撃者が「データ」として入力したはずの言葉が、プログラムの「命令」として解釈されてしまう。これがこの攻撃の恐ろしい正体です。
—
2. なぜ「動的なクエリ生成」が危険なのか
開発現場でよくあるミスが、文字列をパズルのように組み合わせてデータベースへの命令文(SQL)を作ってしまうことです。
— ダメな例:入力されたIDをそのまま埋め込んでいる
“SELECT FROM users WHERE id = ‘” + userInput + “‘;”
もし、ここでuserInputに1' OR '1'='1なんていう魔法の呪文を入れられたらどうなるか。プログラムはこう解釈します。
「ユーザーIDが1の人のデータ、または『1=1(常に真)』を返せ」。
結果、データベース内の全データが丸裸で盗み出されてしまいます。 泥棒に「鍵を開けろ」というメモを渡すのと同じミスを、プログラムのコード上でやってしまっているんです。
—
3. 最強の防犯「プリペアードステートメント」という鍵
ここで登場するのが「プリペアードステートメント(準備された命令文)」という切り札です。
これは、「命令文のテンプレート」を先にデータベースに渡しておく仕組みです。「ここは後で数字が入る場所ですよ」という枠組みだけを作っておき、あとから入力値を「ただのデータ」として流し込むのです。
たとえ攻撃者が「鍵を開けろ」という悪意ある文字列を入力しても、データベースはそれを「あくまで『鍵を開けろ』という名前のID検索」として処理します。そんなIDは存在しないので、何も起きません。泥棒のいたずらは、ドアの外で無効化されるわけです。
実装例:安全な書き方(PHPでの例)
// 1. テンプレートを作る(? が後から値を入れる枠)
$stmt = $pdo->prepare(‘SELECT FROM users WHERE id = ?’);
// 2. 実行時に値を流し込む(これなら悪意ある文字列もただの文字として扱われる)
$stmt->execute([$_GET[‘id’]]);
$user = $stmt->fetch();
これだけで、インジェクション攻撃のほとんどを無効化できます。ORM(Object-Relational Mapping)ライブラリを使っている場合も、ライブラリの機能としてこの仕組みが組み込まれているので、積極的に活用しましょう。
—
4. 今日からできる「守りの姿勢」
最後に、現場で意識してほしいことを3つだけまとめます。
- 「入力値」を信用しない: ユーザーから送られてくるデータは、すべて「泥棒が書いたメモかもしれない」と疑ってください。
- クエリの組み立てはテンプレートで: 文字列連結でSQLを作るのは、今すぐ卒業しましょう。「プリペアードステートメント」を使うのが大人のマナーです。
- ORMに頼る: 安全に作られたフレームワークやORMは、内部で自動的にこの対策を行ってくれます。車輪の再発明をせず、枯れた技術を正しく使いましょう。
まとめ
セキュリティは「完璧」を目指すと息切れしますが、「まずはここから」という急所を抑えるだけで、攻撃者のモチベーションをゴリッと下げることができます。
「プリペアードステートメント」を使うこと。これだけで、あなたは自分の書いたコードという「家」に、頑丈な二重ロックをかけることができます。
難しく考えすぎず、まずは一行、あなたのコードの書き方を変えるところから始めてみませんか?一歩ずつ、着実に安全な開発者になっていきましょう!
コメント