認証と認可の違いとは?ID を書き換えると他人のデータが見える「IDOR」と API の守り方

PR
認証と認可の違いとは?ID を書き換えると他人のデータが見える「IDOR」と API の守り方
この記事は約33分で読めます。

認証と認可は、似た言葉ですが、確かめることが違います。認証(Authentication)は、相手が「名乗っているとおりの人か」を確かめることです。認可(Authorization)は、その人に「その操作やそのデータが許されているか」を確かめることです。ログインできた(認証が通った)人が、そのシステムの何でもしてよいわけではありません。

この違いを抜かして API(アプリやサービスのあいだで、データや機能をやり取りする窓口)を作ると、ログインさえすれば、URL や送るデータの中の ID を書き換えるだけで、他人のデータに届いてしまうことがあります。この弱点を、OWASP(ウェブの安全に取り組む団体)は IDOR(Insecure Direct Object Reference)と呼んでいます。2026年10月には、JPCERT/CC(JPCERT コーディネーションセンター)が「不正アクセスによる個人情報等の漏えい被害が相次いでいます」と注意を呼びかけ、個人情報保護委員会の WARNING は、API の悪用を9つの事例の1つとして挙げています。

この記事は、API やログインのある Web アプリを作り始めた人に向けて、認証と認可の違い、IDOR の仕組み、守り方、401・403・404 の使い分けを書いたものです。書いてあることは、OWASP・個人情報保護委員会・JPCERT/CC・IPA・RFC・MDN の文書を、2026年10月20日に開いて確かめたものです。「直す前と直した後」のコードは、macOS 14.4.1・Python 3.12.3・curl 8.4.0 の手元で流したものを載せています。サーバーは Python の標準ライブラリだけで作り、自分のパソコンの 127.0.0.1 でだけ待ち受けて、確かめが終わったら止めました。フレームワークは使っていません。レートリミットと、データを書き換える・消す操作は、手元では流していません。

OWASP の Cheat Sheet(開発者向けの短い手引き)は頻繁に書き換わります。この記事の「2026年10月時点」は、その日に読んだ内容です。作るときは、最新の文書を開き直してください。

この記事でわかること
  • 認証(相手の確認)と認可(許されているかの確認)の違い
  • 2026年10月に、個人情報保護委員会・JPCERT/CC・IPA が API について書いていること
  • IDOR と BOLA とは何か。管理用・内部の API の話とは別であること
  • 認可を確かめない API と確かめる API を、手元のサーバーで比べた結果(200 が 403 や 404 に変わる)
  • 守り方(サーバー側で毎回・データの持ち主を確かめる・ID を推測しにくくするだけでは足りない)
  • 401・403・404 の使い分けと、「在ることを隠す」ための 404
  1. 認証と認可の違いとは?
  2. なぜ大事?2026年10月の注意喚起(2026年10月時点)
    1. 個人情報保護委員会 WARNING の事例9:ログインしたあと、パラメータを書き換える
    2. JPCERT/CC の「API経由での不正な操作」は、別の話
    3. OWASP Top 10 の1位も、アクセス制御の不備
  3. IDOR(BOLA)とは?
    1. API では BOLA という名前で、1位
  4. 手元で確かめる:直す前と直した後
    1. 準備:サーバー、利用者2人、注文2件
    2. 直す前:認証だけで、認可が無い入口
    3. 直した後(1):全員の注文から引いたあとで、持ち主を確かめる入口
    4. 直した後(2):先に持ち主の注文だけに絞ってから探す入口
    5. ID を URL に出さない入口
    6. UUID(推測しにくい ID)でも、認可が無ければ返る
    7. 手元で確かめていないこと
  5. 守り方:認可の確かめを、どこで、どう入れるか
    1. サーバー側で、リクエストのたびに、既定は拒否
    2. データの持ち主を確かめる
    3. ID を推測しにくくするだけでは、足りない
    4. 返す項目、回数、棚卸し、権限を絞る
    5. 自分のアプリで、テストして確かめる
  6. 401・403・404 の使い分け
  7. よくある質問
    1. BOLA は日本語で何と言いますか?
    2. 他人のサービスで、ID を書き換えて試してもよいですか?
    3. 2026年9月前後の一連の事案は、API の認可の不備が原因ですか?
    4. 認可に失敗したとき、401・403・404 のどれを返せばよいですか?
  8. まとめ
  9. 参考資料

認証と認可の違いとは?

OWASP の Cheat Sheet は、認証を「パスワード・指紋・セキュリティトークンなどで、人やもの、サイトが名乗っているとおりの相手かを確かめること」と説明しています。MDN の日本語版も、認証を「ウェブサイトのユーザーなどの主体が、自称する人物であることを確認するプロセス」と書いています。

認可は、OWASP が NIST(アメリカ国立標準技術研究所)の定義を引いて、「頼まれた操作やサービスが、特定の相手に許されているかを確かめること」と書いています。OWASP は、認可は認証とは別のものだとも書いています。

認証(AuthN)認可(AuthZ)
確かめること名乗っているとおりの相手かその相手に、その操作・そのデータが許されているか
例パスワードやトークンが正しいかこの注文は、ログインしている人のものか
個人情報保護委員会 WARNING の言い方認証(本人確認)認可(そのユーザーが当該データにアクセスする権限を有するかの確認)

OWASP は、この2つが別であることを、次の2つの形で書いています。

  • 認証された(たとえばユーザー名とパスワードでログインした)人でも、システムが技術的にできる操作やデータの全部に、触れてよいとは限りません
  • 逆に、認証が要らないこともあります。ログインしていない人にも、画像やログイン画面、あるいはウェブアプリ全体を見せてよい場合があります

IPA(情報処理推進機構)の「安全なウェブサイトの作り方」(改訂第7版、2021年3月)も、複数の利用者がいるサイトでは、どの利用者にどの操作を許すかを制御する認可制御が要る、と書いています。対策は「認証機能に加えて認可制御の処理を実装し、ログイン中の利用者が他人になりすましてアクセスできないようにする」です。2021年の版なので、新しい内容は IPA や OWASP の最新の文書も見てください。

認証の中身(パスワードの保存、JWT の仕組みなど)は、この記事では詳しく書きません。パスワードの保存はパスワードの正しい受け取り方と保存のしかた:ハッシュ・ソルトと NIST の新しい決まり、JWT はJWT とは?仕組み・セッションとの違い・保存場所の注意点で説明しています。ついでに、OAuth と OpenID Connect の分担も、OWASP の言い方で書いておきます。OAuth は、API へのアクセスを他のアプリに委ねる、認可の枠組みです。OpenID Connect(OIDC)は OAuth の上に作られた身元の層で、署名つきの JWT である ID トークンで、利用者を確かめます。OWASP は「認証やシングルサインオンには OIDC を、API への認可には OAuth を使う」と書いています。

なぜ大事?2026年10月の注意喚起(2026年10月時点)

JPCERT/CC の注意喚起 JPCERT-AT-2026-0030(2026年10月8日公開、10月9日更新)は、次のように書いています。

各被害公表や報道のとおり、直近2026年9月前後で国内組織における不正アクセスによる個人情報等の漏えい被害が相次いでいます。

IPA の注意喚起(2026年10月9日)も、「国内の金融機関や通信事業者等の幅広い事業者から、不正アクセスの被害による情報漏えい事案の公表が相次いで行われています」と書いています。IPA は、特定の製品の脆弱性を狙った攻撃とは断定されておらず、外部に公開しているアプリやサービスの侵害、アカウントの侵害が起点になっていることがうかがえる、とも書いています。取り組むべき対策の1つに、「API連携等アプリケーション構成要素単位での管理状況の見直し」が入っています。

ここで気をつけたいのは、9月の一連の事案のうち、どれが API の認可の不備によるものかは、どの資料にも書かれていないことです。JPCERT/CC は、挙げた手口は寄せられた情報によるもので、「すべての事案で同一の攻撃手法が使用されていることを示すものではありません」と断っています。この記事では、各資料が書いている API の話を、書いてある範囲で並べます。

個人情報保護委員会 WARNING の事例9:ログインしたあと、パラメータを書き換える

個人情報保護委員会は、「WARNING~不正アクセスによる個人データ漏えい防止のための注意喚起~」を、令和6年(2024年)12月11日に出し、令和8年(2026年)10月7日に改訂しました。報告を受けた事案を分析して、9つの事例に分けています。9番目が「APIが悪用される事例」です。事案は名指しされておらず、件数も書かれていません。この記事も、会社名は出しません。

事例9の中身は、次のように書かれています。

個人情報取扱事業者が提供するスマートフォンアプリやWebサービスにおいて、API(…)を利用していたところ、APIの設計・設定不備により、攻撃者が当該サービスにログインした後、パラメータを書き換えることで、本来であれば取得できない他のユーザー情報を取得できる状態となっており、個人データの漏えい等が発生した事案。

「ログインした後」とあるので、ログイン(認証)は通っている場面です。パラメータとは、URL や送るデータの中の値(会員 ID など)のことです。WARNING は、原因を次の4つに分けています。

  • 管理対象から漏れている API があった
  • URL や会員 ID などのパラメータを書き換えることで、他ユーザーのデータにアクセスできる状態だった
  • レスポンスに不要な情報が含まれていたことや、会員 ID が連番だったことから、API の仕様などを推測しやすかった
  • 大量アクセスを制御するための、アクセス回数制限(レートリミット)がなかった

対策例も4つ書かれています。定期的な API の棚卸し、「認証(本人確認)に加え、認可(そのユーザーが当該データにアクセスする権限を有するかの確認)を都度行う仕組み」の実装、不要な情報をレスポンスに含めないことと会員 ID や管理番号にランダムな値(UUID など)を使うこと、短時間の大量アクセスを検知して遮断するレートリミットの実装です。

WARNING は、IDOR や BOLA(後で説明します)という語を使っていません。この記事では、次の節で説明する IDOR と同じ形の話として読みます。この読み方は、この記事のものです。

JPCERT/CC の「API経由での不正な操作」は、別の話

JPCERT/CC の注意喚起には、ケースBとして「API経由での不正な操作」があります。「アプリの管理用APIへの不正なリクエストによって、情報の不正な書き換え等が発生しているものがあります」と書き、報告を受けた手口として、次の3つを挙げています。

  • 一般公開しているスマートフォンアプリを解析して、API の入口(エンドポイント)やキーを特定する
  • 本来、画面操作では実行できない内部 API への攻撃
  • 他のシステムの侵害で盗んだ API キーを使う

この手口は、JPCERT/CC に寄せられた情報によるもので、すべての事案で同じ手口が使われたとは書かれていません。

この3つは、「ID を書き換えて他人のデータを見る」話とは書かれていません。 管理用・内部の API への不正なリクエストの話です。WARNING の事例9(ログインしたあと、パラメータを書き換えてほかの人のデータに届く)とは、別の話として読んでください。JPCERT/CC は、BI ツールや従業員向けの管理システムなど、「不特定多数の利用者からのアクセスを想定していないシステム」も被害に遭っている、とも書いています。社内向けのシステムでも、アクセス制御は要るということです。

OWASP Top 10 の1位も、アクセス制御の不備

OWASP の Top 10:2025 の1位は、A01「Broken Access Control」(日本語版では「アクセス制御の不備」)です。OWASP は、アクセス制御を「ユーザーが意図された権限を超えて行動できないよう、ポリシーを強制すること」と書き、失敗すると、情報の不正な開示、データの改ざんや破壊、権限外の業務機能の実行につながる、と書いています。OWASP は、「テストされたすべてのアプリケーションで、何らかの形でアクセス制御の不備が見つかりました」とも書いています。ただし、この「すべて」が何件のアプリなのかは、そのページに書かれていません。OWASP Top 10:2025 に集まったデータでの話、と受け取ってください。

IDOR(BOLA)とは?

IDOR(Insecure Direct Object Reference)は、OWASP の Cheat Sheet では、「アプリの URL やパラメータにある ID を書き換えることで、データを見たり変えたりできてしまう弱点」とされています。原因は、「その利用者が、そのデータに触れてよいか」を確かめる処理が無いことです。OWASP は、IDOR の材料を3つ挙げています。

材料例
データ口座、書類、問い合わせ、取引、取引先の情報など
そのデータを指す IDID、UUID、口座番号、トークン、slug(URL に使う短い名前)など
データごとの認可の確かめが無いこと「その人が、そのデータに触れてよいか」を確かめていない

OWASP の例は、https://example.org/users/123 のような URL です。この 123 を 124 に変えると他人の情報が見えてしまうなら、アプリは IDOR の弱点を持っています。アプリが、124 番のデータを見てよい人かを、確かめていないからです。ID は、URL だけでなく、POST の本文(隠しフィールド)にあることもあり、連番の数字とは限りません。ファイル名、口座番号、トークンなども ID になります。OWASP の Authorization Cheat Sheet は、この種の弱点を CWE-639(利用者が操れる鍵による認可の回避)として挙げ、開発でとてもよくある弱点で、IDOR の1つの形だと書いています。

API では BOLA という名前で、1位

API の世界では、同じ形の弱点が BOLA(Broken Object Level Authorization)と呼ばれます。意味は「データ単位の認可の不備」です。OWASP API Security Top 10 2023 の1位(API1:2023)で、日本語の版は無いため、この訳はこの記事のものです。IDOR の日本語は、OWASP Top 10:2025 の日本語版では「安全でない直接オブジェクト参照」と訳されています。

OWASP は、BOLA がとてもよくある理由を、次のように書いています。API では、サーバーがクライアントの状態を追い切らず、クライアントから送られてくるデータの ID を使って、どのデータに触れるかを決めがちだからです。ID は、パス、クエリ文字列、ヘッダー、本文のどこにあっても見つけやすいと書かれています。ID をパス(URL の途中)やクエリ(? のあと)で受け取る書き方は、Nest.jsを使ったリクエストパラメータの取得方法【超入門】で説明しています。どの形で受け取っても、確かめが要る点は同じです。

OWASP の言い方では、BOLA は「その API の入口を使うこと自体は許されている」利用者が、ID を書き換えて、データの段で破る形です。使ってはいけない入口(たとえば管理用の機能)に届く場合は、BFLA(Broken Function Level Authorization、機能単位の認可の不備。API5:2023)という別の名前になります。JPCERT/CC のケースBの文は、「本来画面操作では実行できない内部API」で、BFLA の語は使っていません。この記事は、事例9(データ単位)とケースB(管理用・内部の入口)を、別々の話として扱います。

手元で確かめる:直す前と直した後

IDOR を、手元のサーバーで確かめます。試すのは、自分で作った手元のサーバーだけです。 他人のサービスで、ID を書き換えて試してはいけません。不正アクセス行為の禁止等に関する法律(e-Gov 法令検索で読めます)は、第三条で「何人も、不正アクセス行為をしてはならない。」と定めています。不正アクセス行為の定義は、第二条第四項にあります。ID を書き換えて試すことが当たるかどうか(当たるならどの号か)は、この記事では書きません。自分のサーバーと、自分で作った試しのアカウントの中だけで試してください。

準備:サーバー、利用者2人、注文2件

サーバーは、Python の標準ライブラリ(http.server・json・secrets・uuid)だけで書いた、約85行のものです。http.server は、Python の文書が「本番での利用は勧めない。基本的なセキュリティの確かめしかしない」と書いているので、手元の確かめ専用です。127.0.0.1(自分のパソコンの中だけ)で待ち受けました。

試しの利用者は、alice と bob の2人です。トークンとは、ログインしたあとのリクエストに付けて送る、本人の証になる文字列のことです。手元のトークンは、起動のたびに secrets.token_urlsafe(16) で作ります。secrets は、パスワードやトークンのような秘密の値を作るための Python の標準ライブラリです。この記事では、トークンの値は載せず、alice のトークンを $TA、bob のトークンを $TB の変数で書きます。$B は http://127.0.0.1:8765 です。注文は2件で、ID 1 が alice の、ID 2 が bob のものです。ID は連番にしました(WARNING の事例9の原因に、会員 ID が連番だったことが入っています)。

まず、認証の部分です。リクエストの Authorization: Bearer <トークン> から、「誰か」を決めます。

def current_user(self):
    # 認証: Authorization: Bearer <token> から「誰か」を決める
    h = self.headers.get("Authorization", "")
    if not h.startswith("Bearer "):
        return None
    tok = h[len("Bearer "):]
    for t, u in TOKENS.items():
        if secrets.compare_digest(t, tok):
            return u
    return None

トークンが無い、または違うときは、None が返り、サーバーは 401 を返して終わります。JavaScript からこのヘッダーを付けてリクエストを送る書き方はfetch と axios違いのわかりやすい話【JS初心者向け完全入門】で、別のオリジンの API を呼んで「has been blocked by CORS policy」と出たときはCORS エラーとは?「has been blocked by CORS policy」の原因と直し方【メッセージ別】で説明しています。この記事は、サーバー側で確かめる話です。

$ curl -s -i $B/unsafe/orders/1 | grep -i "^HTTP\|^WWW-Authenticate"
HTTP/1.0 401 Unauthorized
WWW-Authenticate: Bearer realm="demo"

$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer not-a-real-token" $B/safe/orders/1
{"error": "unauthenticated"}  -> 401

(HTTP/1.0 は、http.server の既定の版です。-> 401 の部分は、curl の -w で HTTP の番号を出したものです。)

直す前:認証だけで、認可が無い入口

次の入口(/unsafe/…)は、トークンが正しい(認証が通る)ことだけを見ています。以下のコードの user は、current_user() の結果(トークンから決めた利用者)、order は、URL の番号で引いた注文(無ければ None)です。

if mode == "unsafe":
    # 認証だけ。認可(持ち主か)を確かめない
    if order is None:
        return self.send_json(404, {"error": "not found"})
    return self.send_json(200, order)

alice のトークンで、alice 自身の注文 ID 1 と、bob の注文 ID 2 を頼みます。

$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/unsafe/orders/1
{"id": 1, "uuid": "0d2a7d56-6c42-49a9-b647-0f17244e83cb", "owner": "alice", "item": "book"}  -> 200
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/unsafe/orders/2
{"id": 2, "uuid": "e82fca0e-8920-4ebb-8e5b-90df83016c73", "owner": "bob", "item": "pen"}  -> 200

2行目が問題です。alice のトークンで、bob の注文が 200 で返りました。認証は通っていますが、認可を確かめていないので、返ってしまいます。これが、IDOR(BOLA)の形です。なお、応答に owner を入れているのは、見やすくするための例です。必要のない項目を返さないほうがよいことは、後の守り方の節で書きます。

直した後(1):全員の注文から引いたあとで、持ち主を確かめる入口

OWASP の IDOR Cheat Sheet は、直し方を2つの形で書いています。1つ目は、引いたあとで持ち主をはっきり確かめる形("verify ownership explicitly after fetching the object")です。手元の /safe/… の入口は、この形です。全員の注文から ID で引いて、そのあとで、持ち主とトークンから決めた利用者を比べます。以下のコードの order は、全員の注文(ORDERS)から ID で引いたものです。

if mode == "safe":
    # 認証+認可。読んだあとで持ち主を比べる(無い→404、他人→403)
    if order is None:
        return self.send_json(404, {"error": "not found"})
    if order["owner"] != user:
        return self.send_json(403, {"error": "forbidden"})
    return self.send_json(200, order)
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/safe/orders/1
{"id": 1, "uuid": "0d2a7d56-6c42-49a9-b647-0f17244e83cb", "owner": "alice", "item": "book"}  -> 200
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/safe/orders/2
{"error": "forbidden"}  -> 403
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TB" $B/safe/orders/2
{"id": 2, "uuid": "e82fca0e-8920-4ebb-8e5b-90df83016c73", "owner": "bob", "item": "pen"}  -> 200
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/safe/orders/3
{"error": "not found"}  -> 404

alice のトークンで bob の注文(ID 2)を頼むと、200 だったものが 403 に変わりました。 bob 自身のトークンなら 200 です。存在しない ID 3 は 404 です。

この形には、OWASP が書いている性質があります。引いたあとの確かめは、「無い」ものと「触れてはいけない」ものを分けられる("can distinguish a missing object from one the caller cannot access")というものです。手元でも、403 と 404 が分かれているので、alice には「ID 2 の注文は在るが、自分のものではない」「ID 3 は無い」が分かります。データが在ること自体を知られたくないときは、次の形があります。

直した後(2):先に持ち主の注文だけに絞ってから探す入口

2つ目は、OWASP が勧めている形("Prefer a lookup limited to documents owned by the current user")です。いまの利用者が持つデータだけに絞って引く形で、後の守り方の節で書く Ruby on Rails の例と同じ考え方です。OWASP は、データが在ること自体が秘密なら、この絞って引く形(scoped lookup)を使い、「無い」と「他人のもの」のどちらも同じ公開の応答(たとえば 404)にする、と書いています。手元の /scoped/… の入口は、この形です。先にその利用者の注文だけを取り出し、その中を ID で探します。key は URL の最後の部分(注文の番号か UUID)で、kind はそのどちらかを表します。

if mode == "scoped":
    # 持ち主で絞って引く: 先にこの利用者の注文だけにしてから、その中を ID で探す
    mine = {k: o for k, o in ORDERS.items() if o["owner"] == user}
    if kind == "orders":
        order = mine.get(int(key)) if key.isdigit() else None
    else:
        order = {o["uuid"]: o for o in mine.values()}.get(key)
    # 無いのも他人のも、この利用者の範囲には無いので同じ 404(在るかどうかを見せない)
    if order is None:
        return self.send_json(404, {"error": "not found"})
    return self.send_json(200, order)
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/scoped/orders/1
{"id": 1, "uuid": "0d2a7d56-6c42-49a9-b647-0f17244e83cb", "owner": "alice", "item": "book"}  -> 200
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/scoped/orders/2
{"error": "not found"}  -> 404
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/scoped/orders/3
{"error": "not found"}  -> 404
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TB" $B/scoped/orders/2
{"id": 2, "uuid": "e82fca0e-8920-4ebb-8e5b-90df83016c73", "owner": "bob", "item": "pen"}  -> 200

alice の範囲には注文 1 しか無いので、bob の注文(ID 2)も、存在しない注文(ID 3)も、同じ 404 の応答です。応答から「在るかどうか」は分かりません。bob 自身のトークンなら、ID 2 は 200 で返ります。404 の使い分けは、401・403・404 の節で、もう一度書きます。

ID を URL に出さない入口

もう1つ、/me/orders という入口も作りました。URL に ID を出さず、トークンから決めた利用者のデータだけを返します。

$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/me/orders
[{"id": 1, "uuid": "0d2a7d56-6c42-49a9-b647-0f17244e83cb", "owner": "alice", "item": "book"}]  -> 200

alice のトークンでは、alice の注文だけが返ります。OWASP は、可能なら ID を利用者に見せず、いまログインしている人をセッション(または安全に作った JWT)から決めるように勧めています。

UUID(推測しにくい ID)でも、認可が無ければ返る

注文には、UUID(推測しにくい長い ID)も付けてあります。UUID で引く入口も作りました。$UB は bob の注文の UUID で、「何かの形で UUID が知られてしまった」場面の代わりに、手元の台本で渡したものです。

$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/unsafe/by-uuid/$UB
{"id": 2, "uuid": "e82fca0e-8920-4ebb-8e5b-90df83016c73", "owner": "bob", "item": "pen"}  -> 200
$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/safe/by-uuid/$UB
{"error": "forbidden"}  -> 403

先に持ち主の注文だけに絞る入口でも、同じ UUID を試しました。

$ curl -s -w "  -> %{http_code}" -H "Authorization: Bearer $TA" $B/scoped/by-uuid/$UB
{"error": "not found"}  -> 404

認可を確かめない入口は、UUID でも bob のデータを返しました。引いたあとで確かめる入口は 403、先に絞る入口は 404 です。ID が推測しにくいことと、認可を確かめることは、別のことです。UUID とは何かは、【完全入門】UUIDとは?仕様や種類についてすぐ分かる大切な話で説明しています。

手元で確かめていないこと

  • レートリミット(429)。後で書くとおり、複数の資料が挙げていますが、手元のサーバーには入れていません
  • POST・PUT・DELETE(書き換えや削除)の認可。流したのは GET だけです
  • フレームワーク(FastAPI・Express・Rails など)の認可の仕組み。標準ライブラリだけで流しました。FastAPI で API を作る流れは、【Python入門】Fast APIの実装手順とフレームワーク技術選定で説明しています
  • Windows。macOS で流しました。シェルの変数($TA など)の書き方は、Windows の PowerShell やコマンドプロンプトでは違います
  • 本物のデータベースでの絞り込み。手元の /scoped/… は、Python の辞書で絞りました

サーバーのログ(標準エラー)には、流したリクエストと、起動を待つための1本(GET /、401)だけが残っていました。確かめが終わったら、サーバーを止め、8765 番ポートで待ち受けるものが無いことも確かめています。

守り方:認可の確かめを、どこで、どう入れるか

ここからは、OWASP・個人情報保護委員会・IPA・JPCERT/CC が書いている守り方を、順に並べます。

サーバー側で、リクエストのたびに、既定は拒否

認可の確かめは、サーバー側で行います。OWASP は、クライアント側(画面)の確かめを、許すか拒むかの決め手にしてはいけないと書いています。使い勝手のためならよいが、画面側の仕組みは回避されやすいからです。OWASP Top 10 は、アクセス制御が効くのは、攻撃者がチェックを書き換えられない、信頼できるサーバー側のコードの中だけだと書いています。画面で隠していても、サーバーに直接頼まれれば届いてしまう例も、挙げられています。

確かめは、リクエストのたびに行います。AJAX(画面を切り替えずにデータを取る仕組み)からでも、サーバー側からでも、どこから来たリクエストでも同じです。OWASP は「攻撃者は入口を1つ見つければよい」「ほとんどのリクエストで確かめるだけでは足りない」と書き、メソッドやクラスごとに付けるのでなく、アプリ全体で一度に設定できる仕組み(ミドルウェアなど。ミドルウェアとは、リクエストを処理する途中に挟む共通の処理のことです)を勧めています。

もう1つは、既定は拒否(deny by default)です。どの規則にも当たらないときは、拒む側に倒します。権限は、「なぜ与えたか」を言えるものだけにします。手元のサーバーも、どの入口にも当たらない道は 404 にしました。

公開しない REST API(URL とメソッドでデータを操作する形の API)も同じです。OWASP の REST Security Cheat Sheet は、入口(エンドポイント)ごとにアクセス制御をすると書いています。JPCERT/CC の対策も、「非公開APIを含む各APIエンドポイントでアクセス制御を実施し、許可された利用者やHTTPメソッドからのアクセスのみを受け付ける」です。

データの持ち主を確かめる

OWASP API1 は、データの ID を受け取って何かをする API の入口は、どれも「ログインした利用者が、頼まれたデータに、頼まれた操作をしてよいか」を確かめるべきだと書いています。防ぎ方は4つで、利用者の方針に沿った認可の仕組みを作る、クライアントからの入力でデータベースのレコードに触れるすべての関数でその仕組みを使う、レコードの ID に推測できないランダムな値を使うほうがよい、認可のテストを書いて落ちる変更は出さない、です。

「ログイン中の利用者の ID と比べればよい」と言えるかは、場面で違います。

  • IPA は、データベースを検索するための利用者 ID が、ログイン中の利用者 ID と一致しているかを常に確認する(または、利用者 ID を外部から受け取らず、セッションの変数から取る)ように勧めています。これは、利用者 ID そのものを鍵にして、その人のデータを引く画面の話です
  • OWASP API1 は、セッションの利用者 ID(JWT から取り出したものなど)と、ID のパラメータを比べるだけでは、BOLA を直すのに足りない、一部の場合しか直らないと書いています。こちらは、注文や書類のような、利用者とは別のデータの ID の話です(この区別は、この記事の読みです)

どちらの場面でも合う形は、データの持ち主を確かめることです。OWASP の IDOR Cheat Sheet は、主キー(データを一意に指す値)でデータを引くときは、その利用者が触れてよい範囲の中から引くように勧めています。Ruby on Rails の例では、すべてのプロジェクトから探す書き方は弱く、いまの利用者のプロジェクトの中から探す書き方が安全、とされています。IPA も、注文番号の例で、「ログイン中の利用者に閲覧を許可された番号であるかどうかを常に確認する」と書いています。Rails の例と同じ形なのは、手元の /scoped/…(先に持ち主の注文だけに絞る)と /me/orders(ID を出さない)です。/safe/… は、引いたあとで持ち主を比べる形です。データベースに問い合わせる SQL を組み立てるときの注意は、SQL インジェクションの記事で扱います。

ID を推測しにくくするだけでは、足りない

UUID のような推測しにくい ID の重みは、資料で書き方が違います。

資料書き方
個人情報保護委員会 WARNING対策例4つの1つとして、「会員IDや管理番号にランダムな値(UUID等)を採用する」と並べる(優先は書いていない)
OWASP API1防ぎ方の1つとして、ID にランダムで推測できない値を使うほうがよい(Prefer)
OWASP Authorization・IDOR Cheat SheetID を隠したりランダムにしたりする手はあるが、それだけでは一般に足りない。複雑な ID(GUID など)でも認可の確かめは必須で、URL が漏れても止められなければならない。ランダムな ID は「多層の守りの1つ」

どの資料も、ランダムな ID を「認可の代わり」とは書いていません。手元でも、UUID を知っている alice に、認可の無い入口が bob のデータを返しました。ID の複雑さは足してよい守りですが、認可の確かめが先です。なお、OWASP は、利用者 ID もできればランダムに作り、連番や推測できる ID にしないように勧めています。

返す項目、回数、棚卸し、権限を絞る

WARNING の対策例には、認可のほかに3つあります。

  • 不要な情報をレスポンスに含めない。OWASP API Security Top 10 2023 では、データの項目ごとの認可の不備が API3 に入っています。WARNING はこの番号を引いていませんが、並べて読めます
  • レートリミット(短時間の大量のアクセスを検知して止める、アクセス回数の制限)。OWASP は、自動の攻撃ツールの被害を小さくするために、API にレート制限を入れるよう書いています。REST Security Cheat Sheet は、速すぎるリクエストには 429 Too Many Requests を返すと書いています。JPCERT/CC も、ログイン・パスワードリセット・SMS 送信・検索のような悪用されやすい機能に、個別のレート制限を勧めています。ただし、レートリミットは認可の代わりではありません。どの資料も、認可と並べて挙げています
  • 定期的な API の棚卸し。管理から漏れた API があったことが、事例9の原因の1つでした

最小権限も、OWASP が挙げる基本です。利用者には、仕事に要る最小の権限だけを与えます。JPCERT/CC も、API の利用者やトークンに最小限の権限を、トークンに適切な有効期限を付け、漏えいが疑われるトークンをすぐ無効にできるようにする、と書いています。

自分のアプリで、テストして確かめる

OWASP の IDOR Cheat Sheet は、確かめ方も書いています。利用者 A と B を作って、それぞれのデータを作り、A でログインして、リクエストの ID を B のデータのものに変えて頼み、ID が推測できてもできなくても拒まれることを確かめる、という手順です。読む、作る、変える、消す、書き出す、管理の操作の全部で確かめるように書かれています。この記事の手元の確かめは、この形を、自分で作ったサーバーで流したものです。自分のアプリと、自分で作った試しのアカウントでだけ、行ってください。

OWASP は、設計で決めた権限が効いているかのテストを書くこと、単体テストや結合テストに「既定で拒むか」「確かめに失敗したとき安全に終わるか」を入れることも勧めています。OWASP Top 10 も、機能としてのアクセス制御を、開発者と QA の単体・結合テストに入れるように書いています。

401・403・404 の使い分け

認可の失敗を返すときの HTTP のステータスコード(応答の番号)も、整理しておきます。RFC 9110(HTTP の標準)と MDN の説明です。

番号意味場面
401有効な認証の資格情報が無いので、リクエストを適用していない。返すサーバーは WWW-Authenticate ヘッダーを必ず送るトークンが無い・違う
403サーバーはリクエストを理解したが、応じない。資格情報が付いていても、アクセスを許すのに足りないと見たログインはできているが、その操作・データの権限が無い
404対象の今の表現が見つからない、または、在ることを明かすつもりが無いデータが無い。または、在ることを隠したい

401 の英語の名前は "Unauthorized"(直訳は「認可されていない」)ですが、中身は「有効な認証の資格情報が無い」です。 認可の失敗は、403 のほうです。手元でも、トークンが無い・違うときは 401(WWW-Authenticate: Bearer realm="demo" 付き)、alice が bob の注文を頼んだときは 403 でした。

404 には、使い方が2つあります。RFC 9110 は、禁じた対象が在ることを隠したいサーバーは、403 の代わりに 404 を返してよい(MAY)と書いています。MDN も、権限の足りない相手に、在ることを知らせたくなければ、404 を返すことがあると書いています。OWASP の IDOR Cheat Sheet は、データが在ること自体が秘密なら、持ち主で絞って引いて、「無い」と「他人のもの」を同じ応答(たとえば 404)にする、と書いています。手元の /safe/…(引いたあとで確かめる。403 と 404 が分かれる)と /scoped/…(先に絞って引く。同じ 404)が、この2つの形です。

一方、OWASP の REST Security Cheat Sheet の表は、404 を「在らない資源を頼まれたとき」とだけ書き、「他人のもの」を 404 にする形には触れていません。矛盾ではありませんが、その表だけを見ると、「隠すための 404」は出てきません。認可の失敗には必ずこの番号を返す、と決めた一本の決まりは、確かめた資料の中に見つかりませんでした。 403 で教えるか、404 で隠すかは、そのデータが在ること自体が秘密かどうかで選びます。

なお、OWASP の Authentication Cheat Sheet には、ログインの成功に 200、失敗に 403 を返すと、画面の文が同じでも、そのアカウントが在るかどうかが漏れうる、という話があります。これは、ログイン(認証)の場面の話です。ここまでの、データの認可の場面とは、場面が違います。

よくある質問

BOLA は日本語で何と言いますか?

OWASP API Security Top 10 2023 に、日本語の版は無く(2023 の版の言語は英語・ペルシャ語・フランス語・インドネシア語・ポルトガル語です)、BOLA の公式の日本語訳は、確かめた範囲では見つかりませんでした。この記事では「データ単位の認可の不備」と書きましたが、この訳はこの記事のものです。IDOR は、OWASP Top 10:2025 の日本語版に「安全でない直接オブジェクト参照」とあります。

他人のサービスで、ID を書き換えて試してもよいですか?

試さないでください。不正アクセス行為の禁止等に関する法律は、第三条で「何人も、不正アクセス行為をしてはならない。」と定めています。ID を書き換えて試すことが、不正アクセス行為の定義(第二条第四項)に当たるかどうか(当たるならどの号か)は、この記事では書きません。確かめた資料の中に、その解釈を示した公的な文を、開いていないからです。自分で作ったアプリと、自分で作った試しのアカウントの中だけで試してください。

2026年9月前後の一連の事案は、API の認可の不備が原因ですか?

どの資料にも、書かれていません。JPCERT/CC は、挙げた手口がすべての事案で使われたことを示すものではない、と断っています。IPA も、特定のプロダクトやサービスの脆弱性を狙った攻撃とは断定されていない、と書いています。個人情報保護委員会の WARNING の事例9は、報告を受けた事案を分析した類型で、どの事案かは書かれていません。

認可に失敗したとき、401・403・404 のどれを返せばよいですか?

認可の失敗には必ずこの番号、と決めた一本の決まりは、確かめた資料の中に見つかりませんでした。RFC 9110 は、トークンが無い・違う(認証の失敗)なら 401、権限が無いなら 403 とし、在ることを隠したいときは 403 の代わりに 404 を返してよい(MAY)と書いています。404 にそろえるかは、そのデータが在ること自体が秘密かどうかで選びます。

自分のアプリで確かめること(チェックリスト)
  • 認可の確かめを、画面ではなくサーバー側で、リクエストのたびに行っているか(→「サーバー側で、リクエストのたびに、既定は拒否」)
  • どの規則にも当たらないときは、拒む側(既定は拒否)になっているか(→「サーバー側で、リクエストのたびに、既定は拒否」)
  • 公開しない API も含めて、入口ごとにアクセス制御をしているか(→「サーバー側で、リクエストのたびに、既定は拒否」)
  • ID を受け取って何かをする入口は、ログインした利用者が、そのデータに、その操作をしてよいかを確かめているか(→「データの持ち主を確かめる」)
  • UUID のような推測しにくい ID だからと、認可の確かめを省いていないか(→「ID を推測しにくくするだけでは、足りない」)
  • レートリミットを、認可の代わりにしていないか。返す項目の絞り込みと API の棚卸しも、認可と並べて見ているか(→「返す項目、回数、棚卸し、権限を絞る」)
  • 自分で作った利用者 A と B のアカウントで、A のまま B のデータの ID を頼み、拒まれることを確かめているか(→「自分のアプリで、テストして確かめる」)
  • トークンが無い・違うときは 401、権限が無いときは 403、在ることを隠したいデータは 404 と、場面に合わせて返しているか(→「401・403・404 の使い分け」)

まとめ

  • 認証は「名乗っているとおりの相手か」、認可は「その相手に、その操作・そのデータが許されているか」を確かめること。ログインできても、何でもしてよいわけではない
  • IDOR(API では BOLA)は、ID を書き換えると他人のデータに届いてしまう弱点。原因は、データごとの認可の確かめが無いこと。OWASP API Security Top 10 2023 の1位で、OWASP Top 10:2025 の1位(アクセス制御の不備)にも入っている
  • 個人情報保護委員会 WARNING の事例9(ログインしたあと、パラメータの書き換えで他人のデータに届く)と、JPCERT/CC のケースB(管理用・内部の API への不正なリクエスト)は、別の話
  • 手元の確かめでは、認証だけの入口は他人の注文を 200 で返し、持ち主を確かめる入口は 403 または 404 を返した。UUID でも、認可が無ければ返った
  • 守り方は、サーバー側で・リクエストのたびに・既定は拒否で、データの持ち主を確かめること。ID を推測しにくくするだけでは足りない。レートリミットは認可の代わりではない
  • 401 は認証の失敗、403 は権限が無い。在ることを隠したいときは 404 にしてもよい(MAY)
  • 試すのは、自分で作ったアプリと、自分の試しのアカウントだけ

Web 開発で点検することの全体は、Web 開発のセキュリティの基本にまとめています。

参考資料

この記事の内容は、2026年10月20日に開いた文書をもとにしています。OWASP の Cheat Sheet は頻繁に書き換わります。

※この記事は、公式の資料をもとに AI(Claude)で下書きし、2026年10月20日時点の公式の資料と照らして確かめてから公開しています。誤りに気づいたらお問い合わせからお知らせください。

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