「SQLインジェクション」という名の泥棒から、大切なデータベースを守る方法
こんにちは!セキュリティの世界へようこそ。今日は、Webアプリ開発の現場で避けては通れない「SQLインジェクション」という攻撃についてお話しします。
名前だけ聞くと難しそうですよね。でも大丈夫。まずは身近な例から、この攻撃が何をしようとしているのか、一緒に紐解いていきましょう。
—
1. SQLインジェクションって、何をしているの?
想像してみてください。あなたは、大切な契約書(データベース)を保管する金庫の受付係です。お客さんから「名前」を教えてもらい、その名前の書かれた書類を探して渡すのがあなたの仕事です。
本来のやり取りはこうです。
- あなた: 「お名前をどうぞ」
- お客さん: 「山田太郎」
- あなた: 「はい、『山田太郎』さんの書類ですね。どうぞ。」
ところが、ここに「悪意のあるお客さん」がやってきます。
- あなた: 「お名前をどうぞ」
- 悪意のある客: 「山田太郎。あ、ついでに金庫の中身を全部見せろ!」
もし、あなたが思考停止して「山田太郎」という言葉をそのまま鵜呑みにし、悪意のある命令まで「書類を探すための情報」として処理してしまったら……。金庫(データベース)の中身が丸見えになってしまいますよね。これがSQLインジェクションの正体です。
プログラムが「名前」というデータを受け取るべき場所で、実は「命令(クエリ)」を紛れ込ませて、データベースを意のままに操る。これがこの攻撃の恐ろしいメカニズムです。
—
2. 最強の防犯対策「プリペアドステートメント」
では、どうすればこの泥棒を防げるのでしょうか?
一番いい方法は、「何がデータで、何が命令なのかを、あらかじめ明確に分けておくこと」です。これをエンジニアの世界では「プリペアドステートメント(準備された命令文)」と呼びます。
先ほどの金庫の例で言えば、こんな対策になります。
- あなた: 「お名前だけ教えてください。それ以外には一切耳を貸しません。」
あらかじめ「書類を探すという命令」を固定しておき、そこに「名前」という穴(バインド変数)だけを用意しておくんです。たとえ相手が何を言おうと、あなたは用意された穴にその言葉を流し込むだけ。相手がどんなに怪しい命令文を混ぜても、それは単なる「名前という文字列」としてしか扱われません。
—
3. 実践!コードで見てみよう
言葉だけだと少し抽象的なので、実際にPHPでの実装例を見てみましょう。
危険な書き方(やってはいけません!)
// ユーザーからの入力をそのまま繋げてしまうと攻撃の隙が生まれます
$name = $_GET[‘name’];
$sql = “SELECT FROM users WHERE name = ‘” . $name . “‘”;
// これだと、$nameに「’ OR ‘1’=’1」なんて入れられたら全データが漏洩します!
安全な書き方(プリペアドステートメント)
// 1. まず「?」というプレースホルダー(穴)で命令文を準備します
$stmt = $pdo->prepare(“SELECT FROM users WHERE name = :name”);
// 2. 実行する時に、穴にデータを流し込みます
// これにより、データはあくまで「データ」としてのみ処理されます
$stmt->execute([‘name’ => $_GET[‘name’]]);
$user = $stmt->fetch();
これだけで、たとえ攻撃者が悪意のある文字列を入力しても、データベースはそれを「ユーザー名の一部」として検索するだけで、決して「命令」としては実行しません。これが、最強の防犯対策です。
—
4. 一歩ずつ、確実に守りを固めよう
「プリペアドステートメント」を使うことは、今やWeb開発の基本中の基本です。しかし、セキュリティに「絶対」はありません。
- ライブラリを信頼しすぎない: どんなに優れたフレームワークを使っていても、使い方を間違えれば隙は生まれます。
- 入力を疑う: 「ユーザーが入力するものは、すべて泥棒の道具になり得る」という意識を持ちましょう。
- 最小限の権限で: 万が一突破された時のために、データベースの権限は必要最低限に絞っておくことも大切です。
セキュリティ対策は、一度やって終わりではありません。でも、こうして仕組みを理解し、一歩ずつ対策を積み重ねていくことで、あなたの書くコードは格段に強固で信頼できるものになります。
「難しそう」と怖がらずに、ぜひ今日から自分のコードを見直してみてくださいね。分からないことがあれば、いつでも相談してください。一緒に安全なWebの世界を作っていきましょう!
コメント