こんにちは!インフラや開発の現場で日々奮闘している新人の皆さん、セキュリティの世界へようこそ。
今回は、Webアプリケーションの脆弱性ランキングで堂々の第1位に君臨する大ボス、「OWASP Top 10:2021 A01:2021-Broken Access Control(破損したアクセス制御)」について、一緒に紐解いていきましょう。
「アクセス制御の破損」なんて聞くと、なんだか難しそうな呪文のようですよね。でも安心してください。今回は身近な「家の鍵」に例えて、攻撃が起きる仕組みと、それをピタッと防ぐためのスマートな対策を優しく解説していきます。一歩ずつ、確実に学んでいきましょう!
—
1. 「アクセス制御の破損」ってなに? 身近な例えで理解しよう
皆さんが住んでいるマンションを想像してみてください。
自分の部屋の鍵を開けて中に入る。これは当たり前の「アクセス制御」です。では、もし「自分の部屋の鍵番号の数字を『1つ』ずらしたら、隣の部屋に入れてしまった」としたらどうでしょう? さらに、合鍵を作らなくても、URLの部屋番号を書き換えるだけで他人のプライベートな空間を覗き見できたら……。これが、Webの世界で起きている「アクセス制御の破損(Broken Access Control)」です。
セキュリティの現場では、URLのIDを書き換えるだけで他人のデータが見えてしまうような欠陥を IDOR(Insecure Direct Object Reference:安全ではない直接オブジェクト参照) と呼びます。
攻撃者は特別なハッキングツールを使わずとも、ブラウザのURL欄をちょっと書き換えたり、通信内容(リクエスト)を少しだけいじったりして、他人のクレジットカード情報や個人情報をごっそり盗み出します。「ログインしているから大丈夫」という油断が、この悲劇を生む最大の原因なんです。
—
2. なぜこの脆弱性が生まれてしまうのか?(よくある開発の落とし穴)
新人の開発者さんがよくやってしまいがちな失敗は、「ログインしていれば(認証されていれば)、どんなデータにアクセスしても良いだろう」と、認可(権限のチェック)をサボってしまうことです。
ここで、「認証」と「認可」の違いを整理しておきましょう。
- 認証(Authentication): 「あなたは誰ですか?」を確認すること(ログイン処理)
- 認可(Authorization): 「あなたはそのデータに触る権限を持っていますか?」を確認すること(権限チェック)
ログイン(認証)はクリアしたけれど、「このデータ、本当にこの人が触っていいもの?」という確認(認可)をプログラムが忘れているとき、システムは簡単に突破されてしまいます。
—
3. 泥棒の手口を覗き見:IDORの瞬間
例えば、ユーザーが自分のプロフィール画面を見るためのPHPコードがあったとします。こんなコードを見たことはありませんか?
<?
// 良くない例:渡されたIDのユーザー情報をそのままデータベースから取得して表示している
$userId = $_GET['id']; // URLの ?id=3 などの値を受け取る
// 誰でもこのクエリを実行できてしまう!
$sql = "SELECT * FROM users WHERE id = " . $userId;
$userData = executeQuery($sql);
displayProfile($userData);
?>
このコードの何が問題か分かりますか?
ログインしているユーザーが誰であろうと、URLに ?id=1 と入れれば管理者(ID:1)の情報が見えてしまい、?id=2 と入れれば隣の人の情報が見えてしまいます。これでは、玄関の鍵が常に全開のままで「誰でも好きな部屋に入っていいよ」と言っているようなものです。
—
4. 対策の切り札:リソースベースのアクセス制御と集中化
この脆弱性を根絶するために、僕たちエンジニアが取るべきアプローチは2つあります。
1. 認可チェックの集中化(共通パーツ化する)
2. リソースベースのアクセス制御(RBAC / ABACの導入)
毎回バラバラの場所で「この人、権限あるんだっけ?」と書いていると、必ず書き忘れ(実装漏れ)が起きます。だからこそ、アクセス制御のルールを1箇所に集める(集中化する)ことが鉄則です。
実際に動く安全なコード例(PHP)
先ほどの危険なコードを、しっかりと認可チェックを行う安全なコードに書き換えてみましょう。
<?
// 安全な例:セッションのユーザーと、取得しようとしているデータの所有者が一致するか確認する
session_start();
// 1. まずは「認証」:ログインしていなければ追い返す
if (!isset($_SESSION['login_user_id'])) {
header("Location: /login.php");
exit;
}
$currentLoginUserId = $_SESSION['login_user_id']; // ログイン中の自分のID
$requestedUserId = $_GET['id']; // 表示しようとしているID
// 2. 「認可」のチェック:自分自身のデータか、あるいは管理者の権限を持っているか?
if ($currentLoginUserId != $requestedUserId && !isAdministrator($currentLoginUserId)) {
// 権限がない場合はエラーを返す(アクセス制御の集中関数を使うのもGOOD)
http_response_code(403);
echo "アクセス権限がありません。";
exit;
}
// 3. 権限チェックを通過した場合のみ、安全にデータを取得する
$userData = getUserDataSecurely($requestedUserId);
displayProfile($userData);
?>
このように、「URLのIDを信用せず、現在ログインしているユーザーのセッション情報と突き合わせる」ことが、IDORを防ぐための最大の防御壁になります。
—
5. 本格的なアクセス制御モデル:RBAC と ABAC
システムの規模が大きくなってきたら、個別のコードでチェックするだけでなく、専用のモデルを導入するのがプロのやり方です。
- RBAC(Role-Based Access Control:役割ベースのアクセス制御)
- 「管理者」「一般ユーザー」「ゲスト」といった「役割(Role)」ごとに、触れる機能やデータを割り当てる仕組みです。最も一般的で分かりやすいモデルです。
- ABAC(Attribute-Based Access Control:属性ベースのアクセス制御)
- 「ユーザーの役職」「所属部署」「アクセスの時間帯」「データの機密度」など、さまざまな「属性(Attribute)」を複雑に組み合わせて、より柔軟にアクセス可否を判断する高度な仕組みです。
現場の要件に合わせて、どのモデルを採用するかをアーキテクチャ設計の段階でしっかり見極めましょう。
—
まとめ:一歩ずつ、セキュアな開発を習慣にしよう
今回は、OWASP Top 10の「破損したアクセス制御(IDOR)」について、家の鍵の例えを交えながら解説しました。
セキュリティ対策の基本は、「ユーザーからの入力を絶対に信用しないこと」、そして「認証のあとに必ず認可のチェックを挟むこと」です。
最初は難しく感じるかもしれませんが、日々のコーディングで「おっと、このデータにアクセスするとき、本当にこの人がオーナーか確認したっけ?」と立ち止まる習慣をつけるだけで、あなたの書くコードは劇的に安全になります。
一歩ずつ、確実にセキュアな開発スキルを身につけていきましょう!それでは、また次回のセキュリティ解説でお会いしましょう。
コメント