ゆめさくセキュリティ

レッスン 2-4

認可の設計

認証は入口だけ、認可はすべての操作に要ります。

目安 60 分 トラック 2 Web アプリケーションの 4 本目

読み終えたらすべての操作に認可が要る理由を、例を挙げて説明できる

この頁の流れ

  1. 1なぜ通るのか
  2. 2脆いコードと直したコード
  3. 3ログでの見え方と検知
  4. 4直し方と確かめ方
  5. 5関わる事例
  6. 6確認クイズ
  7. 7説明課題
  8. 8一次資料
  9. 9次へ

URL の番号を変えるだけで他人の注文が見える穴 (IDOR) を扱います。ログインの確かめ (認証) と、その人がその物を触ってよいかの確かめ (認可) を分け、すべての操作で持ち主を突き合わせる作りと、2 人の利用者で互いに届かないことを固める試験までを扱います。

出典を確かめた日 2026年10月24日

なぜ通るのか

通販の画面で、自分の注文を開くと住所の末尾に番号が付いています。/orders/1 です。この番号を 1 つずらして開いたら、知らない人の名前と住所が出てきた。IDOR (直接の参照の確かめ漏れ) と呼ばれる穴は、こうして見つかります。

ログインはできています。サーバーは「この人は alice だ」と知っています。それでも他人の注文が出るのは、サーバーが「この注文は alice の物か」を確かめていないからです。

認証と認可は別の問い

認証は「あなたは誰か」を確かめます。認可は「その人が、その物に、その操作をしてよいか」を確かめます。OWASP の Authorization Cheat Sheet も、認可を認証とは別の手続きとして扱っています。

ログインの画面を固くしても、認可は 1 つも増えません。認証は入口で 1 回、認可は注文を見る時、取り消す時、住所を変える時と、操作のたびに要ります。

番号は利用者の手の中にある

URL の番号、隠しフィールド、JSON の中の ID は、どれも利用者が書き換えられる値です。画面に他人の注文へのリンクを出していなくても、番号は送れます。OWASP の Cheat Sheet は、口座番号のような内部の ID が住所の問い合わせ部分などに出ること自体はよくあると書き、だからこそ推測されても触れないように作るよう勧めています。

連番は推測しやすいので、番号を長いランダムな値にする手もあります。ただしこれは重ねる守りで、確かめの代わりにはなりません。番号が漏れた時点で同じ穴が開きます。

見る時だけ確かめる、が穴になる

よくあるのは、注文の詳細を見る所では持ち主を確かめているのに、取り消しや住所の変更の所では番号だけで動いている形です。画面からは見る所しか押せないので、作った人は気づきにくい。けれども要求は画面を通らずに直接送れます。

脆いコードと直したコード

ラボの中の小さな関数で比べます。Python に付いている sqlite3 だけで動き、外とは通信しません。注文 1 は alice の紅茶、注文 2 は bob の緑茶です。

番号だけで取り出すか、持ち主と突き合わせるか

脆いコード番号だけで注文を取り出す
def get_order(db, user, order_id):sql = ("SELECT id, owner, item"" FROM orders WHERE id = ?")return db.execute(sql, (order_id,)).fetchone()
直したコードログインした人と持ち主を突き合わせる
def get_order(db, user, order_id):sql = ("SELECT id, owner, item"" FROM orders WHERE id = ?"" AND owner = ?")return db.execute(sql, (order_id, user)).fetchone()

脆い方は user を受け取っているのに使っていません。alice が 2 を頼むと、bob の緑茶が返ります。直した方は、持ち主が alice でない注文は 1 件も返しません。「無い」と「あなたの物ではない」を同じ返事 (見つからない) にすると、他人の番号が実在するかどうかも漏れません。

操作ごとに書くか、1 か所の関所を通すか

脆いコード取り消しは番号だけで動く
def cancel(db, user, order_id):db.execute("UPDATE orders SET st = 'cancel'"" WHERE id = ?", (order_id,))
直したコードどの操作も同じ関所を通す
class NotFound(Exception):pass def owned(db, user, order_id):row = db.execute("SELECT owner FROM orders"" WHERE id = ?",(order_id,)).fetchone()if row is None or row[0] != user:raise NotFound(order_id) def cancel(db, user, order_id):owned(db, user, order_id)db.execute("UPDATE orders SET st = 'cancel'"" WHERE id = ?", (order_id,))

直した方は、持ち主の確かめを owned の 1 か所にまとめ、変える操作はかならずそこを通します。操作を足すたびに確かめを書き写す作りだと、どこか 1 つで書き忘れます。OWASP の Cheat Sheet は、どの規則にも当たらない時は拒む (既定で拒否) ことと、要求のたびに権限を確かめることを勧めています。

ログでの見え方と検知

脆いコードのまま、alice が番号を 1 つずつずらして開くと、記録には成功が並びます。行の 2 つ目は頼んだ人、最後は取り出した注文の持ち主です。この持ち主の列を足しておくと、頼んだ人と持ち主が食い違っていることが一目で分かります。

記録脆いコードのときの記録
127.0.0.1 alice GET /orders/1 200 alice127.0.0.1 alice GET /orders/2 200 bob127.0.0.1 alice GET /orders/3 200 carol

直したあとは、2 と 3 が 404 になります。守る側は、次の物を記録と見張りに入れます。

見る物なぜ効くか
頼んだ人と持ち主の食い違い成功した漏れを、そのまま拾える
1 人が短い間に開く別々の番号の数番号を順に試す動きは、ふだんの利用と形が違う
403 と 404 の急な増え方CISA などは、これを番号の総当たりと結び付けている

ACSC、CISA、NSA の共同の注意喚起は、改ざんの試みを記録して警告を出すこと、ふだんの基準を作って外れた動きを見つけることを勧めています。404 と 403 が番号の総当たりの手口と結び付くことにも触れています。

直し方と、直ったことの確かめ方

自分の PC の中だけで行います。外のサービスや、他人の機械には向けません。

  1. 作業用のフォルダに sqlite3 のデータベースを作り、alice の注文 1 と bob の注文 2 を入れる
  2. 脆い get_order に alice と 2 を渡し、bob の注文が返ることを確かめる
  3. 脆い cancel に alice と 2 を渡し、bob の注文が取り消されることを確かめる
  4. 両方を直したコードに置き換え、同じ入力で何も返らず、取り消しも止まることを確かめる
  5. 下の試験を足し、直す前のコードに戻すと試験が落ちることも確かめる
  6. 終わったら、作業用のフォルダごと消す
Python2 人で互いに届かないことを固める試験
def test_alice_cannot_read_bob(db):assert get_order(db, "alice", 2) is Nonerow = get_order(db, "bob", 2)assert row[2] == "緑茶" def test_alice_cannot_cancel_bob(db):with pytest.raises(NotFound):cancel(db, "alice", 2)assert status(db, 2) == "open"

試験は、利用者を 2 人用意し、互いの物に届かないことを両方向で固めます。自分の物は見えることも同時に確かめるので、何でも拒む壊れ方も見逃しません。OWASP の Cheat Sheet も、認可の誤りは小さくても重い結果になるとして、単体と結合の試験を書くよう勧めています。

番号をランダムな値に替えるのは、このあとに重ねる守りです。共同の注意喚起は、URL に ID を出さない間接の参照や、UUID のような強いランダムな値を勧めつつ、UUID は安全のための仕組みに使うべきではないとも注記しています。順番を間違えないでください。先に確かめ、そのうえで推測しにくくします。

関わる事例

この穴に合う事例は、事例の一覧にはまだありません。代わりに、この穴の種類について公的機関が出した文書を紹介します。

2023年7月27日、豪州通信電子局の豪州サイバーセキュリティセンター (ACSC)、米国の CISA、NSA は、IDOR についての共同の注意喚起 (AA23-208A) を出しました。文書は、IDOR を、ほかの正しい利用者の ID を指定した要求で、データの書き換え、削除、読み出しを許してしまうアクセス制御の穴だと説明しています。よくある穴で、開発の過程の外では防ぎにくく、大きな規模で悪用できるため、データの漏えいで頻繁に使われるとも書いています。

作り手への勧めは、このレッスンの直し方と同じ向きです。既定で拒み、データを変える、消す、慎重に扱うべきデータを読む、どの要求にも認証と認可の確かめを入れること、です。

確認クイズ

1 問目

注文の番号を連番から UUID に替えました。持ち主の確かめは入れていません。これで足りますか。

答えだと思う選択肢を押すと、その場で正誤と理由が開きます。

2 問目

詳細を見る画面では持ち主を確かめています。取り消しの API は画面のボタンからしか呼ばれません。取り消しにも確かめは要りますか。

答えだと思う選択肢を押すと、その場で正誤と理由が開きます。

説明課題

次のことを、自分の言葉で 3 行にまとめてください。書けたら、このレッスンは修了です。

  • なぜ認可は、ログインの時ではなく、すべての操作で要るのか。例を 1 つ挙げて
模範解答を見る

ログインで分かるのは「誰か」だけで、「その物を触ってよいか」は物と操作ごとに違う。たとえば注文の詳細では持ち主を確かめていても、取り消しの API が番号だけで動けば、番号を書き換えた要求で他人の注文を取り消せる。だから持ち主の確かめは 1 か所の関所にまとめ、すべての操作がそこを通るように作る。

一次資料

どれも 2026年10月24日に開いて、題と中身を確かめました。

次へ

次のレッスンは「サーバーを踏み台にした内部への到達」です。外の URL を取りに行く機能が、内側への入口になる穴を扱います。

次のレッスン 2-5 サーバーを踏み台にした内部への到達

このレッスンにつながる物