今回のテーマは、OWASP Juice ShopのBasket機能を使って、IDOR / BOLA / Broken Access Controlの考え方と検証手順を学ぶこと。対象は自分で管理しているローカルのJuice Shopのみとし、正常な操作からHTTP通信を確認し、Object IDを変更したときにServerが正しくAuthorizationを行うかを検証した。
## フェーズ1:正常なBasket操作を確認 まずJuice Shopの商品を1つ `Add to Basket` した。目的は、正常な操作をしたときにBrowserがどんなHTTP RequestをServerへ送るか確認すること。 Firefox Developer ToolsのNetworkでXHR通信を確認すると、以下のRequestが見つかった。 `POST /api/BasketItems/` Request Bodyには以下の値が含まれていた。 `ProductId` `BasketId` `quantity` 例: `{"ProductId":1,"BasketId":"5","quantity":1}` 意味: `ProductId`:どの商品を追加するか。 `BasketId`:どのBasketに追加するか。 `quantity`:数量。 この時点で、Browser側から `BasketId` を指定してServerへ送っていることが分かった。 ペンテスター目線では、`UserId`、`BasketId`、`OrderId`、`DocumentId` などのObject IDがRequestに含まれていたら、Authorization確認の候補になる。
## フェーズ2:正常Requestと改変Requestを比較 正常なRequestを右クリックし、Firefoxの `Edit and Resend` を使用した。 目的は、正常なRequestを基準にして、1箇所だけ値を変更したときにServerのResponseがどう変わるか確認すること。 今回は `BasketId` のみ変更した。 例: `BasketId:"5"` → `BasketId:"1"` 結果: `Invalid BasketId` この結果から分かるのは、指定したBasket IDがServerに受け入れられなかったということだけ。 この時点では、IDOR対策が正しく行われているとは断定できない。Basket ID自体が存在しない可能性もあるため。 ペンテストでは「値を変更できた」だけでは脆弱性ではない。改変後に権限外のObject操作が成立したかまで確認する必要がある。
## フェーズ3:Basket IDの正体を確認 Network内に `GET 6` というRequestがあったため詳細を確認した。 Request URL: `GET /rest/basket/6` これにより、URL末尾の `6` がBasket IDであることを確認した。 Responseには以下のような情報があった。 `id: 6` `UserId: 25` つまり、Basket ID 6がUser ID 25に紐づいていることが確認できた。 ここで重要なのは、URLに直接Object IDが含まれていること。 `/rest/basket/6` のような形式は、IDOR / BOLAを確認する際に注目する典型的なパターン。
## フェーズ4:読み取り側のIDOR / BOLAを検証 次に `GET /rest/basket/6` を `Edit and Resend` で開き、Basket IDだけ変更した。 `/rest/basket/6` ↓ `/rest/basket/7` 目的は、自分のログイン状態のまま別Basket IDを指定したときに、ServerがそのBasketを返すか確認すること。 ここで確認しているのは、読み取り側のAuthorization。 正常な場合: `401` `403` `Invalid BasketId` `data:null` などで拒否または非表示になる。 脆弱な場合: `200 OK` かつ別UserのBasket内容が返る。 重要なのは、Status CodeだけではなくResponse Bodyも確認すること。
## フェーズ5:別Basketへのアクセス結果を確認 `/rest/basket/7` を送信すると、Responseは以下だった。 `200 OK` `status: "success"` `data: null` これはRequest自体は処理されたが、Basket 7の実データは返らなかったことを意味する。 この結果だけでは、Basket 7が存在しないのか、Authorizationで隠されているのかは判断できない。 そのため、別のBasket IDも確認した。 その後、Juice Shop画面に花吹雪とChallenge Success表示が出た。
## フェーズ6:Juice ShopのChallenge成功を確認 Score Boardを開くと、以下のChallenge成功が確認できた。 `View Basket` `View another user's shopping basket.` これは、別UserのBasketを閲覧する条件を満たしたことを意味する。 セキュリティ上の分類は、Broken Access Control / IDOR / BOLA系。 正式名称: IDOR = Insecure Direct Object Reference BOLA = Broken Object Level Authorization Broken Access Control = アクセス制御の不備 今回の本質は、Object IDを変更しただけで、本来アクセスできない別UserのObjectへ到達できたこと。
## フェーズ7:AuthenticationとAuthorizationの違い 今回の脆弱性を理解するうえで重要なのが、AuthenticationとAuthorizationの違い。 Authentication = 認証 「あなたは誰か」を確認する。 例: EmailとPasswordでログインする。 JWTを確認してUserを特定する。 Authorization = 認可 「そのUserがその操作をしてよいか」を確認する。 例: User 25がBasket 6を見る権限があるか。 User 25がBasket 7を見る権限があるか。 今回の問題は、ログイン自体は正常でも、Object単位のAuthorizationが不十分だったこと。
## フェーズ8:ホテルの部屋で考える 初学者向けに置き換えると、Basketはホテルの部屋番号のようなもの。 Authentication: 「あなたはこのホテルの宿泊客ですね」 Authorization: 「あなたが入っていいのは6号室ですね」 脆弱な状態では、宿泊客であることだけ確認し、 「7号室に入っていい人か」 を確認していない。 Webでは以下の形になる。 `JWTあり` ↓ ログイン済みUserと確認 ↓ `GET /rest/basket/7` ↓ 所有者確認をせずBasket 7を返す これがBroken Access Control。
## フェーズ9:正常なServerが行うべき処理 安全なServerでは、Objectへアクセスするたびに所有者を確認する。 例: Request: `GET /rest/basket/7` Server: JWTからUser IDを取得 ↓ `UserId = 25` Database: Basket 7の所有者を確認 ↓ `Basket 7 owner = UserId 42` 比較: `25 != 42` ↓ 拒否 一方、 `Basket 6 owner = UserId 25` なら、 `25 == 25` ↓ 許可 となる。
## フェーズ10:具体的なセキュリティ対策 最も重要な対策は、Clientから送られたObject IDを信用せず、Server側で毎回Authorizationを確認すること。 対策の基本: JWTやSessionから現在のUserを特定する。 Databaseで対象Objectの所有者を確認する。 現在Userと所有者が一致するか確認する。 一致しなければ拒否する。 Frontendだけで制限しない。 読み取り、作成、更新、削除すべてで確認する。 例: `UserId 25` ↓ `BasketId 7` ↓ Basket 7のOwnerをDB確認 ↓ Ownerが25なら許可 違えば `403 Forbidden`
## フェーズ11:より安全なAPI設計 さらに安全にする方法として、UserにObject IDを自由に指定させない設計がある。 例: `GET /rest/basket/6` ではなく、 `GET /rest/basket/me` とする。 ServerはJWTからUserを特定し、そのUserのBasketをDatabaseから取得する。 流れ: JWT ↓ UserId取得 ↓ UserのBasketをDBから取得 ↓ Response これにより、Userが `6 → 7 → 8` のようにIDを変更する機会を減らせる。 ただし、この設計でもServer側のAuthorization確認は必要。
## フェーズ12:UUIDにすれば安全というわけではない 連番ID: `6` `7` `8` は推測しやすい。 UUID: `550e8400-e29b-41d4-a716-446655440000` のような値は推測しにくい。 ただし、UUIDを使うだけでは根本対策にならない。 他人のUUIDが何らかの形で漏れた場合、Serverが所有者確認をしていなければアクセスできてしまう。 つまり、 `推測しにくいID` ≠ `Authorization` 本質的な対策は、Server側でObjectごとの権限確認を行うこと。
## フェーズ13:今回学んだペンテストの基本フロー 今回の一連は、Web/APIペンテストで使われる基本的な流れ。 正常な機能を操作 ↓ NetworkでHTTP Requestを確認 ↓ Object IDを探す ↓ 正常Requestを保存 ↓ IDを1箇所だけ変更 ↓ 再送 ↓ Status CodeとResponse Bodyを比較 ↓ 権限外Objectへアクセスできるか確認 ↓ 脆弱性を判定 この流れはBasketだけではなく、以下にも応用できる。 `UserId` `AccountId` `OrderId` `InvoiceId` `DocumentId` `MessageId` `AddressId`
## フェーズ14:ペンテスターが見るポイント Web ApplicationやAPIでIDを見つけたら、 「このIDを別Userのものに変更したらどうなるか」 を考える。 ただし、重要なのは単なるID変更ではなく、 「Serverが所有者確認をしているか」 を見ること。 今回のテーマを一言でまとめると、 Authentication = あなたは誰か Authorization = あなたはそれをしてよいか IDOR / BOLA = Object IDを直接指定したとき、Authorization確認が不足していないかを検証する手法 対策 = Server側でObjectごとの所有者・権限を毎回確認すること。
## フェーズ15:今回の重要ポイントまとめ 今回の検証で重要なのは、URLやRequest BodyにあるIDそのものではなく、**ServerがそのIDに対する権限確認をしているか**という点。`/rest/basket/6` のようにObject IDが直接指定されている場合、ペンテスターは別IDへ変更したときのResponseを確認する。脆弱性が成立するのは、ログイン済みの自分が、本来アクセスできない別UserのObjectを読み取る・変更する・削除するなどの操作に成功した場合。 防御側は、JWTやSessionから現在のUserを特定し、Database上のObject所有者やRoleと照合してから処理を許可する必要がある。Frontend上でボタンを隠す、IDを長くする、UUIDにする、といった対策だけでは不十分。**最終的なAuthorization判定は必ずServer側で行う**ことが基本になる。
## 今回の用語整理 IDOR = Insecure Direct Object Reference。Userが指定できるObject IDを変更することで、本来アクセスできないObjectへアクセスできてしまう問題。 BOLA = Broken Object Level Authorization。APIでObject単位のAuthorizationが正しく行われていない状態。IDORと非常に近い概念。 Broken Access Control = 本来許可されていない操作やDataへのアクセスを防げていない状態の総称。 Authentication = 認証。「あなたは誰か」を確認する。 Authorization = 認可。「あなたはその操作をしてよいか」を確認する。 Object = Basket、Order、User、Documentなど、Application内で個別に扱われるData。 Object ID = そのObjectを識別する番号や文字列。例:`BasketId 6`。 JWT = JSON Web Token。今回の環境では、Login後のUserを識別するための認証情報としてHTTP Requestの `Authorization` Headerで使用された。 `401 Unauthorized` = 一般的には認証情報がない、または無効な場合に使われるHTTP Status Code。 `403 Forbidden` = 認証済みでも、その操作を行う権限がない場合に使われるHTTP Status Code。 `200 OK` = HTTP Requestの処理自体が成功したことを示す。ただし、認証成功や脆弱性成立を意味するわけではないためResponse Bodyも確認する必要がある。
## 今回使用したFirefox Developer Toolsの機能 Network:BrowserとServerのHTTP通信を確認する。 XHR:Web Applicationが裏側で行うAPI系通信を絞り込む際に使用。 Headers:Request URL、HTTP Method、Authorization、Cookieなどを確認する。 Request:BrowserからServerへ送ったDataを確認する。 Response:Serverが返したDataを確認する。 Edit and Resend:取得した正常Requestの一部を変更して再送する。Burp SuiteのRepeaterに近い使い方ができる。
## 最終整理 今回の流れは、`Add to Basket` という正常操作から始まり、Networkで `POST /api/BasketItems/` と `GET /rest/basket/6` を発見し、`BasketId` がObject IDとして使われていることを確認した。その後、Firefoxの `Edit and Resend` でBasket IDだけを変更してResponseを比較し、Juice Shopの `View Basket - View another user's shopping basket.` Challengeを達成した。 この検証で学んだ中心テーマは、**「ログインできているか」だけではSecurityとして不十分であり、「そのUserがそのObjectへアクセスしてよいか」をServer側で確認する必要がある**ということ。 ペンテスター側では、Object IDを発見したら別Objectへのアクセス可否を検証する。防御側では、Requestごとに現在UserとObject所有者・権限を照合する。これがIDOR / BOLA / Broken Access Controlを理解する基本になる。