【入門編】 認可の昇格を狙う権限分離の不備(RBAC/ABAC) – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

こんにちは!Webアプリケーションの開発やインフラの管理、毎日お疲れ様です。

システムを作るとき、「この機能は一般のユーザーさんだけ」「こっちは管理者さんだけ」というように、誰がどこまで触れるかを決める仕組みを作りますよね。いわゆる「権限管理」というやつです。

実はこの権限管理、ちょっとした設計ミスやうっかりミスが原因で、一般のユーザーがこっそり管理者になってしまうという恐ろしい事態(特権昇格)を引き起こしてしまうことがあるんです。

今回は、セキュリティの世界の裏側も知る私と一緒に、この権限分離の不備(RBAC/ABACの設計ミス)がなぜ起きるのか、そしてどうやってそれを防げばいいのかを、身近な例えを交えながら一歩ずつ優しく学んでいきましょう!

—

1. 家の鍵に例えて考える「認可(Authorization)」の仕組み

まずは、「認証」と「認可」の違いからおさらいしておきましょう。セキュリティの話でよく出てくるこの2つ、最初は混同しやすいですよね。

  • 認証(Authentication):「私は〇〇です」と証明すること。
  • *例え:* 家の玄関のドアに「合鍵」を差し込んで、自分がその家の住人だと証明することです。
  • 認可(Authorization):証明した人が「何をしていい人なのか」の権限を確認すること。
  • *例え:* 家に入ったあと、リビングには自由に入っていいけれど、開かずの金庫がある「秘密の部屋」には、お父さんとお母さん(管理者)しか入れない、というルールを決めることです。

今回焦点を当てるのは、この後者の「認可」の部分です。
もし、泥棒(悪意ある攻撃者)が一般の住人の合鍵(一般ユーザーのアカウント)を手に入れたとします。その泥棒が、鍵の掛かっていない「秘密の部屋」のドアノブをガチャリと回して中に入れてしまったら……大変ですよね。これが、システムにおける「権限分離の不備」なんです。

—

2. RBACとABACってなに? 難しく考えずに仕組みを理解しよう

権限を管理する代表的な仕組みとして、RBAC(ロールベースアクセス制御)とABAC(属性ベースアクセス制御)というものがあります。名前は難しそうですが、中身はシンプルです。

RBAC(Role-Based Access Control)

  • 考え方: 「役職(ロール)」ごとにできることを決める仕組みです。
  • *例え:* 会社で「一般社員」の人はコピー機と自分のデスクだけ、「部長」の人はそれに加えて人事評価ファイルが見られる、というようにグループ分けするやり方です。

ABAC(Attribute-Based Access Control)

  • 考え方: 「状況や属性(条件)」を細かく見て判断する仕組みです。
  • *例え:* 部長であっても、「平日の昼間(時間)」に、「会社のパソコン(場所)」からアクセスしているときだけ、人事評価ファイルを開いてもいいよ、という厳しい条件をつけるやり方です。

システムを作る際、「この人は一般ユーザーだからこのページはダメ」と、RBACのグループ分けやABACの条件チェックのどこかに穴(設計ミス)があると、攻撃者にそこを突かれてしまうのです。

—

3. なぜ起きる? 攻撃者が狙う「権限の穴」

実際のWebアプリで、よくある失敗例を見てみましょう。
例えば、一般ユーザーが自分のプロフィール画面を見るためのURLが、次のようなものだったとします。

https://example.com/user/profile?id=105

ここで、もしあなたが意地悪な攻撃者だったらどう考えるでしょうか?
「もしかして、この id=105 の数字を id=1 に書き換えたら、管理者のプロフィールや設定画面が見えちゃうんじゃないか?」と試してみたくなるはずです。

もし、このURLにアクセスされたとき、システムが「今ログインしている人は、本当にこの id=1 の情報を覗いていい人なのかな?」というチェック(認可の確認)を忘れているとどうなるでしょう?
なんと、一般ユーザーの権限のまま、管理者のデータをごっそり盗み見たり、書き換えたりできてしまうのです。これが、現場で最もよくある「IDOR(安全でない直接オブジェクト参照)」と呼ばれる権限の不備です。

—

4. 実装で防ぐ! 最小権限の原則と安全なコード例

では、こうした悲劇を防ぐためにはどうすればよいのでしょうか?
基本の考え方は「最小権限の原則(Least Privilege)」です。これは、「人は必要最低限の鍵だけ持ち、それ以外はすべて開けられないようにする」という防犯の鉄則と同じです。

PHPを使った簡単なサンプルコードで、正しい認可チェックの書き方を見てみましょう。

❌ 危険なコード例(チェックをサボっている状態)

<?php
// ユーザープロフィールを表示する処理(危険!)
$userId = $_GET['id']; // URLから送られてきたIDをそのまま受け取る

// データベースからユーザー情報を取得して画面に表示する
$userInfo = getDatabaseRow($userId);
echo "ようこそ、" . $userInfo['name'] . " さん!";
// 問題点:ログイン中のユーザーが「本当にこのIDの情報を閲覧する権限があるか」を一切確認していません!
?>

⭕ 安全なコード例(きちんと認可チェックを入れている状態)

<?php
session_start();
// 1. まず、今ログインしているユーザーのIDとロールをセッションから確実に取り出す
$currentUserId   = $_SESSION['user_id'] ?? null;
$currentUserRole = $_SESSION['user_role'] ?? 'guest';

// 2. URLから閲覧したい対象のIDを取得する
$targetUserId = $_GET['id'] ?? null;

// 認証チェック:ログインしていなければログイン画面へ跳ね返す
if (!$currentUserId) {
    header("Location: /login.php");
    exit;
}

// 3. 【重要】「今ログインしている人」が「見ようとしている人」と一致するか、あるいは管理者権限を持っているか厳しくチェックする!
if ($currentUserId != $targetUserId && $currentUserRole !== 'admin') {
    // 権限がない場合は、エラーを出して処理をピタッと止める
    http_response_code(403); // アクセス禁止を示すHTTPステータスコード
    die("エラー:このページにアクセスする権限がありません。");
}

// 4. チェックを無事にクリアした場合のみ、安全にデータを取得して表示する
$userInfo = getDatabaseRow($targetUserId);
echo "ようこそ、" . htmlspecialchars($userInfo['name'], ENT_QUOTES, 'UTF-8') . " さん!";
?>

このように、データを表示したり変更したりする手前で、「本当にこの操作をしていい身分(権限)なのかな?」とプログラムのなかで何重にも確認することが、何よりも強力な防御になります。

—

5. まとめ:一歩ずつ、安全なシステムを作っていきましょう

今回は、RBACやABACといった権限分離の仕組みと、それが破られてしまう特権昇格のメカニズムを、身近な例えとコードを交えて解説しました。

  • 認証だけでなく、「認可(何ができるかのチェック)」を必ずセットで実装する。
  • URLのパラメータなどを信用せず、「今操作している人は誰で、どんな権限を持っているか」をサーバー側で常に確認する。
  • 自分に与えられた最低限の権限だけで作業する「最小権限の原則」を意識する。

最初は難しく感じるかもしれませんが、日々の開発のなかで「この処理、本当に権限チェック入ってるかな?」と一瞬立ち止まる習慣をつけるだけで、セキュリティ事故のリスクはグッと減らすことができます。

一歩ずつ、確実に安全なシステムづくりを楽しんでいきましょう!それではまた次回の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました