はじめに
例えば、自分のユーザー情報を見るためのAPIがあるとします。
GET /users/1
この「1」の部分を「2」に変えたらどうなるでしょうか。
もしそのまま他人の情報が見えてしまうなら、それはバグです。
そして、このタイプの問題は実務でもよく起きます。
この記事では、この問題がなぜ起きるのかをコードで確認しながら整理していきます。
まずは何が起きているのか
次のようなAPIを考えます。
GET /users/1
レスポンス:
{
"id": 1,
"name": "Taro"
}
特に変わったことはしていない、よくあるAPIです。
問題のある実装
サーバー側のコードがこうなっていたとします。
<?php
$id = $_GET['id'];
$user = findUserById($id);
echo json_encode($user);
このコード、動きとしては正しいです。
指定されたIDのユーザーを取得して返しているだけです。
何が問題か
この状態でログインしているユーザーが
GET /users/2
のようにリクエストすると、どうなるか。
→ そのままユーザー2の情報が返ってきます。
つまり、
「ログインしているユーザーが誰か」と「取得するデータ」が結びついていない
状態です。
なぜこうなるか
このコードは
- データを取得する処理
しかしていません。
一方で、
- そのデータにアクセスしていいか
は一切確認していません。
ここが抜けると、IDを変えるだけで他人のデータにアクセスできてしまいます。
修正する
やることはシンプルで、「誰のデータか」をチェックします。
<?php
session_start();
$userId = $_SESSION['user_id'];
$targetId = $_GET['id'];
if ($userId != $targetId) {
http_response_code(403);
exit;
}
$user = findUserById($targetId);
echo json_encode($user);
これで、自分のデータ以外にはアクセスできなくなります。
よくあるパターン
この問題は、特別な場面だけで起きるわけではありません。
例えば:
- ユーザー情報
- 注文履歴
- ファイルダウンロード
- 管理画面
「IDを指定して何かを取得する」処理がある場所なら、どこでも起きます。
IDを隠しても意味がない
たまに
/users/1 → /users/aj3f9k
のようにIDを隠すケースがあります。
これ自体は悪くないですが、これだけでは解決しません。
アクセス制御がなければ、値を推測された時点で同じ問題が起きます。
まとめ
今回の問題は、特別な攻撃というより
「アクセス制御を書いていない」
ことが原因です。
ログインしているかどうかだけでなく、
「そのデータにアクセスしていいのか」
まで含めて実装する必要があります。
このあたりを意識しておかないと、気づかないうちに穴ができるので注意が必要です。