こんにちは!セキュリティの世界へようこそ。
新人のIT担当者や、これからWeb開発を本格的に学んでいく皆さん、「API」という言葉を最近よく耳にするのではないでしょうか?
アプリがスマホとサーバーの間でデータをやり取りする仕組みとして、今のWeb開発には欠かせない技術です。とても便利で魔法のような仕組みですが、実はここに、初心者がうっかり見落としがちで、しかし攻撃者には真っ先に狙われる「大きな盲点」が存在します。
それが今回テーマにする 「BOLA(Broken Object Level Authorization)」 という脆弱性です。
名前を聞くだけで難しそうですよね。でも大丈夫です。今回は「家の鍵と泥棒」に例えながら、攻撃者がどうやってこの脆弱性を突いてくるのか、そして私たちがどうやってそれを防げばいいのかを、一緒に一歩ずつ優しく紐解いていきましょう!
—
1. 家の鍵でイメージする「BOLA」の正体
まず、BOLAがどういうものか、身近な例えで考えてみましょう。
想像してみてください。あなたはとあるマンションの一室(部屋番号: 101)に住んでいます。お部屋にはしっかり鍵がかかっていて、あなた以外の人は勝手に入れないはずですよね。
ある日、あなたは自分のスマホアプリを使って、注文したお弁当の配達状況を確認しようとしました。アプリの画面を見ると、URLや通信データ(API)のなかに次のような番号が含まれていました。
https://api.example.com/orders/101
この 101 という数字は、「101号室の注文データ」を指しています。あなたは当然、自分の注文データを見ているつもりです。
ここで、ちょっとイタズラ心を出した(あるいは悪だくみをした)攻撃者が現れました。彼はこう考えます。
「待てよ、この 101 って数字、もし 102 に書き換えたら、隣の人の注文データや個人情報が見えちゃうんじゃないか?」
試してみると、サーバーはこう返してきました。
「はい、どうぞ!これが102号室のデータですよ」
……恐ろしいことに、誰でも入れる合鍵があちこちにばら撒かれているような状態、これが BOLA です。
日本語に直すと「オブジェクトレベルの認可不備」と言いますが、要するに 「ログインはしているけれど、他人のデータまで自由に見られてしまう(鍵の管理がガバガバな)状態」 を指します。
—
2. 攻撃者はどうやってBOLAを見つけ、悪用するのか?
実際のWebアプリやスマホアプリの裏側では、ユーザーの行動に合わせてAPIリクエストが飛び交っています。攻撃者は、ブラウザの開発者ツール(F12キーを押して出る画面)や、通信を覗き見するプロキシツールを使って、このやり取りをじっと観察しています。
ここで、脆弱性を作り込んでしまった「危ないPHPコード」の例を見てみましょう。開発者の視点で、「何がダメなのか」を確認してみてください。
危ないAPIの実装例(PHP)
<?
// データベースからユーザーの注文情報を取得するAPIのつもり
// 接続設定などは省略しています
// リクエストパラメータから「注文ID」を受け取る
$order_id = $_GET['order_id'];
// 【危険!】誰がこのリクエストを送ってきたか(ログイン中のユーザー)を確認せず、
// 指定された ID のデータをそのままデータベースから引っこ抜いている!
$query = "SELECT * FROM orders WHERE id = " . $order_id;
$result = mysqli_query($conn, $query);
$order_data = mysqli_fetch_assoc($result);
// JSON形式で結果を返す
header('Content-Type: application/json');
echo json_encode($order_data);
?>
このコードの何が問題かわかりますでしょうか?
確かにこのAPIにアクセスするにはログインが必要(認証はクリアしている)かもしれません。しかし、「今リクエストを送ってきたユーザーが、本当にその注文データの持ち主なの?」 というチェック(これが「認可」です)がごっそり抜け落ちています。
これでは、悪意あるユーザーがブラウザのURLやAPIのパラメータを書き換えるだけで、日本中、あるいは世界中のユーザーの機密情報を総取りできてしまいます。ペネトレーションテスト(侵入テスト)の現場でも、この手の不備は非常に高い確率で見つかる定番の脆弱性です。
—
3. どうやって防ぐの? 対策の基本と実装アプローチ
「うわ、うちのコードももしかして……」と思ったそこのあなた、焦る必要はありません。今から正しい書き方を覚えれば大丈夫です!
BOLAを防ぐための鉄則はたった一つ。
「データを渡す前に、必ず『このデータ、本当にこのユーザーのものだっけ?』と二重で確認(認可チェック)する」 これに尽きます。
先ほどの危ないコードを、安全な形に修正してみましょう。
安全なAPIの実装例(PHP)
<?
// セッションから「現在ログインしているユーザーのID」を取得する
session_start();
$current_user_id = $_SESSION['user_id']; // ログイン時に安全に保存されたID
// リクエストパラメータから「注文ID」を受け取る
$order_id = $_GET['order_id'];
// 【安全!】「注文ID」だけでなく「所有者のユーザーID」も条件に含めて検索する!
// これにより、他人の注文を指定されてもデータがヒットしなくなります。
$stmt = $conn->prepare("SELECT * FROM orders WHERE id = ? AND user_id = ?");
$stmt->bind_param("ii", $order_id, $current_user_id);
$stmt->execute();
$result = $stmt->get_result();
$order_data = $result->fetch_assoc();
if (!$order_data) {
// データが見つからない、または他人のデータの場合はエラーを返す
http_response_code(403);
echo json_encode(["error" => "アクセス権限がありません、またはデータが存在しません。"]);
exit;
}
// 正常なデータを返す
header('Content-Type: application/json');
echo json_encode($order_data);
?>
このように、データベースを検索する段階で user_id(誰のものか)を必ず条件に縛り付けることで、IDを勝手に書き換えられても他人のデータには絶対にアクセスできない仕組み(堅牢な認可)が作れます。
—
4. セキュリティヘッダーや全体的な防衛の考え方
コードレベルでの対策に加えて、API全体を守るためのインフラや設計の工夫も大切です。
1. 推測しにくいID(UUID)の採用
今回のように 101, 102, 103 といった連番のIDを使っていると、攻撃者が次にどんなIDがあるかを簡単に想像できてしまいます。代わりに、550e8400-e29b-41d4-a716-446655440000 のような、ランダムで予測不可能な UUID(v4) を主キーに採用することで、IDの推測攻撃自体の難易度をグッと上げることができます。
2. APIゲートウェイや認可ミドルウェアの活用
個別のプログラムファイルごとに認可チェックを書き忘れるヒューマンエラーを防ぐために、共通のアクセス制御レイヤー(API Gatewayなど)を挟み、リクエストの正当性を一元的に検証するアーキテクチャも現代のWeb開発では非常に有効です。
—
まとめ
今回は、BOLA(Broken Object Level Authorization)の仕組みと、その特定・悪用・対策についてお話ししました。
- BOLAとは: APIのIDを書き換えるだけで、他人のリソースにアクセスできてしまう認可の不備。
- 攻撃の狙い目: 連番のIDや、所有者チェックの甘いAPIエンドポイント。
- 対策の基本: 「認証(あなたは誰?)」だけでなく、必ず「認可(このデータに触る権利はある?)」をコードの裏側で厳格にチェックすること。
セキュリティ対策は、一度覚えたら終わりではなく、日々の開発のなかに習慣として溶け込ませることが何よりも大切です。
「このパラメータ、もし別の値に変えられたらどうなるだろう?」という攻撃者の視点をちょっとだけ頭の片隅に置きながら、安全で信頼されるシステムを一緒に作っていきましょう!
一歩ずつ、確実にスキルアップしていけば大丈夫です。次の記事でも、実践的ですぐに役立つセキュリティの知識をお届けしますので、ぜひ楽しみにしていてくださいね!
コメント