こんにちは!Webアプリケーション開発の世界へようこそ。
新人のIT担当者さんや、これからセキュリティの勉強を始めるという開発者さんのなかには、「セキュリティってなんだか難しそう……」「専門用語ばかりで頭がパンクしそう……」と感じている方も多いのではないでしょうか?
でも、安心してくださいね。セキュリティの本質は、実は私たちの日常生活にある「防犯」の仕組みとまったく同じなんです。
今回は、Webアプリ開発で本当によくやらかしてしまう、そして攻撃者にとっては「格好の獲物」になりやすいIDOR(Insecure Direct Object Reference:認可制御の欠如)という脆弱性について、身近な例えを交えながら優しく紐解いていきたいと思います。一歩ずつ、一緒に学んでいきましょう!
—
1. 身近な例えで考えてみよう:ホテルの「ルームキー」とIDOR
まずは、IDORがどんなものなのか、ホテルの宿泊に例えて考えてみましょう。
あなたが旅行でホテルに泊まったとします。チェックインを済ませて、自分の部屋(301号室)に入りました。さて、ここでちょっと悪戯心を出して(あるいはうっかり)、隣の302号室のドアノブをガチャリと回してみたとします。
……もし、ここで302号室のドアが何の鍵もかけずに開いてしまい、他人の荷物や財布が丸見えだったらどうでしょう?
「うわ、やばい! 鍵が壊れてるよ!」と焦りますよね。これがまさに、Webの世界におけるIDORの正体です。
Webアプリで起きていること
Webサイトを作るとき、私たちはよくURLに番号(ID)を振ります。例えば、ユーザーのマイページを表示するURLが次のようなものだったとします。
https://example.com/user/profile?id=1001
ここで、もしあなたがログインしているユーザー(ID: 1001)なのだとしたら、ブラウザのURLバーにある id=1001 を id=1002 に書き換えて、エンターキーを押したらどうなるでしょうか?
もし、サーバー側が「おっ、1002番ね、はいどうぞ!」と、他人のプライベートなプロフィールや購入履歴をそのまま返してしまったとしたら……?
これが、IDOR(認可制御の欠如)という脆弱性です。攻撃者はこの仕組みを使って、次々と他人のデータを覗き見したり、勝手に書き換えたりしてしまうのです。
—
2. なぜこの脆弱性が生まれてしまうのか?
「認証」と「認可」という言葉、聞いたことはありますか? この2つは似ているようで、セキュリティにおいてはまったく違う意味を持ちます。ここを理解するのが、IDORを防ぐ第一歩になります。
- 認証(Authentication): 「あなたは誰ですか?」を確認すること。(例:パスワードやIDを使ったログイン)
- 認可(Authorization): 「あなたには、そのデータを見る権限がありますか?」を確認すること。(例:その情報にアクセスしていいのは本人だけか?)
新米プログラマーがやってしまいがちな失敗は、「ログインさえしていれば(=認証さえしていれば)、どのデータにアクセスしても大丈夫だろう」と勘違いしてしまうことです。
先ほどのホテルの例で言えば、「ホテルに入館するチェックイン(認証)は済ませたんだから、どの部屋のドアを開けても文句言われないはずだ!」と思い込んでいる状態ですね。これでは泥棒に入り放題になってしまいます。
—
3. 実装の現場で見る:危ないコードと安全なコード
それでは、実際にPHPを使ったサンプルコードを見て、どう直せばいいのかを確認してみましょう。
⚠️【危険な実装例】IDをそのまま信じ込んでいるコード
まずは、やってはいけない「危ないコード」です。URLから受け取ったIDの数値を、そのままデータベースの検索に使っています。
<?php
// セッションからログイン中のユーザーIDを取得(一応ログインはしている)
session_start();
$current_user_id = $_SESSION['user_id'] ?? null;
// URLパラメータから表示したいユーザーのIDを取得する
// 例: profile.php?id=1002
$target_id = $_GET['id'] ?? null;
// 【危険ポイント】
// ログインしているかどうかしかチェックせず、
// 「今見ようとしているデータが、本当にこの人のものか?」を確認していない!
$pdo = new PDO('mysql:host=localhost;dbname=test_db', 'root', 'password');
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$target_id]);
$user_data = $stmt->fetch();
// データの表示
echo "<h1>" . htmlspecialchars($user_data['name'], ENT_QUOTES, 'UTF-8') . "さんのマイページ</h1>";
echo "<p>メールアドレス: " . htmlspecialchars($user_data['email'], ENT_QUOTES, 'UTF-8') . "</p>";
?>
このコードの何が問題か分かりますか?
ログインしているユーザーが $_SESSION['user_id'] を持っているにもかかわらず、URLの $_GET['id'] を盲信してしまっているため、URLの数字を書き換えるだけで、誰でも他人のデータを自由に見られてしまうのです。
—
✨【安全な実装例】サーバー側でしっかり「認可」をチェックするコード
では、これをどう直せばいいでしょうか? 答えは簡単です。「URLのIDなんて信用せず、今ログインしているユーザー自身のIDを使ってデータを取得する」、あるいは「URLのIDを指定する場合でも、ログイン中のユーザーがそのデータにアクセスする権限を持っているかを厳しくチェックする」ことです。
マイページであれば、URLに他人のIDを指定させる必要すらありません。
<?php
// セッションの開始
session_start();
// ログインしていなければログイン画面へリダイレクト
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
// 【安全ポイント】
// URLのパラメータではなく、確実に安全な「セッション内のID」を使ってデータを取得する!
// これにより、他人のデータを見ることが物理的に不可能になります。
$current_user_id = $_SESSION['user_id'];
$pdo = new PDO('mysql:host=localhost;dbname=test_db', 'root', 'password');
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$current_user_id]);
$user_data = $stmt->fetch();
// もしデータが見つからない場合の処理
if (!$user_data) {
echo "ユーザーデータが見つかりません。";
exit;
}
// データの表示
echo "<h1>" . htmlspecialchars($user_data['name'], ENT_QUOTES, 'UTF-8') . "さんのマイページ</h1>";
echo "<p>メールアドレス: " . htmlspecialchars($user_data['email'], ENT_QUOTES, 'UTF-8') . "</p>";
?>
このように、サーバー側で「今この操作をしている人と、操作対象のデータが本当に一致しているか」を毎回しっかりと突合(チェック)することが、IDORを防ぐための最大の秘訣です。
—
4. まとめ:今日からできる一歩
IDOR(認可制御の欠如)は、攻撃者からすると非常にシンプルに見つけやすく、かつ被害が大きくなりやすい危険な脆弱性です。しかし、開発する側からすれば、「URLのパラメータを鵜呑みにせず、サーバー側でアクセス権限を必ず検証する」という基本を徹底するだけで、綺麗に防ぐことができます。
- 「認証」だけでなく「認可」を意識する(ログインしている=何を見てもいい、ではない)
- URLやフォームの隠しフィールド(hidden)の値を信用しない
- データの取得時には、必ずセッションのユーザーIDと照合する
セキュリティの対策は、一度にすべてを完璧にするのは難しいものです。でも、こうして一つひとつの仕組みを知り、コードを書くときに「あれ、これって他人のIDを指定されたらどうなるだろう?」と少し立ち止まって考える習慣をつけるだけで、あなたの作るWebアプリケーションは劇的に安全になります。
一歩ずつ、確実にセキュアな開発スキルを身につけていきましょう!応援しています!
コメント