A01 アクセス制御の不備

認証は誰かを決め、認可はしてよいかを決める。A01 は、認可がサーバーの側で、要求のたびに、対象の1件ごとに効いていないときに起きる。

OWASP Top 10:2025 A01 Broken Access Control。2021年版から1位のまま。対応するCWEは40で、2021年版の A10 SSRF を取り込んだ。検査したアプリの100%に何らかの形で見つかった[1]。2026年11月時点の記載で書いています。

要点

  • 判定は、攻撃者が書き換えられないサーバー側のコードで行う。公開の資源以外は既定で拒否する。
  • 判定の材料のうち、要求の側が決められるものを信じた時点で穴になる。URLのID、隠しフィールド、端末の画面の制御、検証していないトークンの中身。
  • 型は4つに分けて見る。対象の認可が無い IDOR、機能の認可が無い強制ブラウズ、本人の権限を借りる CSRF、サーバーの位置を借りる SSRF。
  • 判定は1か所で作って全部の入口で使う。データの問い合わせそのものに持ち主の条件を入れる。

成立の条件

認可の判定は、主体、操作、対象の3つの組で決まる。どれか1つでも要求の側の言い分で決まると、他人の権限で動ける。

要求は認証を通ったあと、主体と操作と対象の組で認可を受けてからデータに届く。認可の3つの軸のどれかが欠けると通り抜けられる 要求 中身は全部改変可 認証 誰か。A07 認可。A01 主体役割、所属する組織 操作読む、変える、消す 対象この1件の記録 データ 機能 穴の出方 対象を見ていないログインさえしていれば、IDを替えて他人の記録に届く 操作を見ていないGET は守り、DELETE や PUT は付け忘れる 判定が端末にしかない画面でボタンを隠すだけで、APIは誰の要求でも通す
図1 認可の3つの軸と、欠けたときの穴
  • 要求の側が決められる値を判定に使う パスやクエリのID、隠しフィールド、Cookie、検証しないトークンの中身。OWASP は JWT と Cookie と隠しフィールドの書き換えを典型に挙げる[1]。
  • サーバーがその値を主体の権限と突き合わせない ログインしているかだけを見て、その記録の持ち主かは見ない。
  • 判定が全部の入口に掛かっていない 別のメソッド、古い版のAPI、管理用の経路、静的ファイル。チートシートは静的な資源にも認可を掛けるよう求める[2]。

型1 対象の認可が無い IDOR

記録を指す値を差し替えるだけで他人の記録に届く。IDOR のチートシートは、対象、それを指す値、対象ごとの認可の欠落の3つがそろうと成り立つと書く[3]。

利用者Aが請求書のIDを1002に替えて要求すると、ログインだけを確かめるアプリが、利用者Bの請求書を返す 利用者A ログイン済み GET /invoices/1002 アプリ ログインしているかは見る 持ち主かは見ない 請求書の表 1001持ち主 A 1002持ち主 B 1003持ち主 C Bの請求書がAに返る
図2 IDOR が通る道筋

悪い例。架空の請求書のAPI

app.get('/invoices/:id', requireLogin, async (req, res) => {
  const inv = await db.invoices.findOne({ id: req.params.id });
  res.json(inv);
});

直した例。持ち主の条件を問い合わせに入れる

app.get('/invoices/:id', requireLogin, async (req, res) => {
  const inv = await db.invoices.findOne({
    id: req.params.id,
    tenantId: req.user.tenantId,
    ownerId: req.user.id,
  });
  if (!inv) return res.status(404).end();  // 他人の記録か存在しないかを区別させない
  res.json(inv);
});
  • 読んでから持ち主を比べる書き方より、問い合わせに条件を入れるほうが、一覧、更新、削除の全部に同じ条件が掛かりやすい。OWASP は、記録の持ち主を強制するモデルを求める[1]。
  • IDを UUID にしても直らない。推測しにくくなるだけで、値が漏れれば届く。チートシートも、推測された場合や漏れた場合でも認可で止めるよう求める[2][3]。
  • IDはパスだけでなく、本文、隠しフィールド、ヘッダ、GraphQL の引数、一括操作の配列の中にも来る。

型2 機能の認可が無い強制ブラウズ

管理の機能を画面で隠し、APIの側で役割を確かめない。OWASP の例は、端末のJavaScriptで止めているだけの管理画面へ、コマンドから直接要求を送るものだ[1]。

一般の利用者の画面には削除のボタンが無いが、同じ利用者が直接DELETEの要求を送ると、役割を確かめないAPIが実行する 一般の利用者の画面 削除のボタンを描かない 同じ利用者の直接の要求 DELETE /admin/users/42 押せないので来ない 来る API GET /admin/usersadmin DELETE /admin/users/:id確認なし 経路ごとに付けると付け忘れる
図3 画面で隠しただけの機能に直接届く道筋

悪い例。経路ごとに確かめを付ける

router.get('/admin/users', requireRole('admin'), listUsers);
router.delete('/admin/users/:id', deleteUser);  // 付け忘れ。既定で通る

直した例。表に無い経路は拒否し、判定を1か所に置く

const policy = {
  'GET /admin/users':        ['admin'],
  'DELETE /admin/users/:id': ['admin'],
};
app.use((req, res, next) => {
  const roles = policy[routeKeyOf(req)];  // 照合した経路の型。例 DELETE /admin/users/:id
  if (!roles || !roles.includes(req.user?.role)) return res.status(403).end();
  next();
});
  • 役割の名前を、署名を確かめていないトークンや Cookie から読まない。役割はサーバー側の記録から引く。署名の確かめ方は A04 で扱う。
  • 同じ資源の全部のメソッドと、古い版のAPI、管理用のホスト名、ファイルの直接の配信に同じ表を掛ける。
  • 役割だけでは足りない場面が多い。チートシートは、役割だけで決める RBAC より、属性や関係で決める ABAC や ReBAC を勧める[2]。

型3 本人の権限を借りる CSRF

利用者がログインしたまま罠のページを開くと、端末が Cookie を付けて、状態を変える要求を正規のサイトへ送る。サーバーから見れば本人の要求と区別が付かない。CWE-352 として A01 に入っている[1]。

利用者の端末が罠のページを読み込み、自動で送られるフォームが、Cookieを付けて正規のサイトの設定変更の要求になる 罠のページ trap.example.net 利用者の端末 app.example.com に ログイン中の Cookie 開くと自動で送る form POST app.example.com POST /settings Cookie が付いている 本人として受理 止める点 送り元の確認とトークン。Cookie だけで判断しない
図4 CSRF が通る道筋

直した例。状態を変えるメソッドに一括で掛ける

function csrfGuard(req, res, next) {
  if (['GET', 'HEAD', 'OPTIONS'].includes(req.method)) return next();  // GET で状態を変えない前提
  const site = req.get('Sec-Fetch-Site');
  if (site && site !== 'same-origin') return res.status(403).end();
  if (!site && req.get('Origin') !== 'https://app.example.com') return res.status(403).end();
  if (!tokenMatches(req.get('X-CSRF-Token'), req.session.csrfToken)) return res.status(403).end();
  next();
}
  • チートシートの推奨は、状態を持つ作りなら同期トークン、持たない作りなら署名してセッションに結び付けた二重送信 Cookie。新しい端末だけを相手にするなら Fetch Metadata の Sec-Fetch-Site で他サイトからの要求を落とせるが、送らない端末のために Origin の確認へ落とす道が要る[4][10]。
  • Cookie の SameSite 属性は多層の守りとして足す。単独で頼らない[4]。
  • GET で状態を変える経路があると、上の守りが全部外れる。

型4 サーバーの位置を借りる SSRF

URLを受け取ってサーバーが取りに行く機能で、行き先を利用者が決められる。サーバーは内側の網に居るので、外から届かない管理画面やクラウドのメタデータの窓口に要求を出させられる。2021年版では A10 として独立していた。

攻撃者が渡したURLをアプリが取りに行き、外から届かない内側の管理APIやメタデータの窓口に要求が届く。出口の制限で止める 攻撃者 URLを渡す 内側の網 アプリ 画像をURLから取り込む Webhook を呼ぶ 出口 内側の管理API 192.0.2.10 メタデータの窓口 一時の鍵を返す 出口で行き先を絞れば、アプリの検査を抜けても届かない
図5 SSRF が通る道筋と、網の出口で止める点

悪い例

const r = await fetch(req.body.imageUrl);  // 行き先も手順も転送も利用者次第

直した例。行き先が決まっている場合は許可の一覧で決め、転送を追わない

const ALLOWED_HOSTS = new Set(['images.example.com']);
const u = new URL(req.body.imageUrl);
if (u.protocol !== 'https:' || !ALLOWED_HOSTS.has(u.hostname)) return res.status(400).end();
const r = await fetch(u, { redirect: 'error' });  // 転送先で一覧を迂回させない
  • チートシートは、行き先が決まる場合と任意の外部へ出す場合を分ける。決まる場合は許可の一覧で、任意の場合は名前を引いた先のIPが許す網か確かめ、確かめたIPへそのまま接続する。確かめと接続の間に名前を引き直すと、DNS rebinding で抜けられる[5]。
  • 文字列の否定の一覧で localhost や内側のアドレスを弾く作りは、URLの解釈の違いで抜けられる。チートシートは許可の一覧を求める[5]。
  • 網の層でも、アプリのサーバーが出てよい先だけを通す。AWS では IMDSv2 を必須にし、IMDSv1 を止める[5][11]。
  • XML の外部実体参照からも SSRF が起きる。A02 の XXE を合わせて見る。

守り方

根本の直し方
  • 認可は信頼できるサーバー側のコードで行い、公開の資源以外は既定で拒否する[1][2]。
  • 判定を1か所で作り、全部の入口で使う。経路やメソッドを足すたびに表へ足し、表に無ければ落ちる形にする。
  • データの問い合わせに、組織と持ち主の条件を入れる。モデルの層で持ち主を強制する。
  • 状態を変える要求には、枠組みの CSRF の守りを一括で掛ける。
  • サーバーから外へ出る要求は、行き先を許可の一覧か、許す網の確認で決め、網の出口でも絞る。
  • 単体と結合の試験に、他人のIDと低い権限での呼び出しを入れる[1][2]。
緩和の策
  • 認可の失敗を記録し、繰り返しを管理者へ通知する。A09 とつなぐ。
  • APIの呼び出しの回数を制限し、IDの総当たりの被害を減らす。
  • Webサーバーのディレクトリの一覧を切り、.git とバックアップのファイルを公開の場所に置かない。
  • セッションはログアウトでサーバー側から無効にし、状態を持たない JWT は寿命を短くする[1]。

IPA の「安全なウェブサイトの作り方」も、脆弱性ごとに原因そのものをなくす根本的な解決策と、影響を減らす策を分けて示している[8]。

診断で見る所

許可を得た範囲で行う。範囲の決め方は 診断の許可と範囲 にある。試験の手順は WSTG の認可の試験と、ASVS 5.0 の V8 Authorization が詳しい[6][7]。

観点用意と試し方当たりの兆候
対象の認可同じ役割の利用者AとB、別の組織の利用者Cを用意し、Aの要求のIDをBとCのものに替える。パス、クエリ、本文、ヘッダ、一括操作の配列の全部200 で他人の中身が返る。更新が通る。一覧に他人の行が混ざる
機能の認可管理者で記録した要求を、一般の利用者と未ログインで送り直す。全部のメソッドと古い版のAPI403 か 401 でなく処理される
判定の材料役割や組織のIDが、Cookie、隠しフィールド、トークンの中身にあるか探し、書き換える書き換えた値で権限が変わる
CSRF状態を変える要求から、トークンと Origin を外す。GET で状態が変わる経路を探す外しても受理される
SSRFURL、ホスト名、Webhook、取り込み、PDFの生成を受け取る引数を洗い出す。範囲の中の、自分が管理する受け口へ向けて、サーバーから要求が来るか見るサーバーから受け口へ要求が来る。転送を追う
置き忘れディレクトリの一覧、.git、バックアップ、一時ファイル公開の場所から取れる

コードのレビューでは、データを読む関数を全部挙げ、それぞれに主体の条件が入っているかを1つずつ見る。自動の走査は対象の認可をほとんど見つけられない。業務の上で誰の記録かを知らないからだ。

関連するCWE

A01 に対応する40のうち、設計とレビューでよく当たるもの[1][9]。

CWE名前この頁の型
CWE-284Improper Access Control全体の親
CWE-862Missing Authorization型1、型2
CWE-863Incorrect Authorization型1、型2
CWE-639Authorization Bypass Through User-Controlled Key型1
CWE-566Authorization Bypass Through User-Controlled SQL Primary Key型1
CWE-425Direct Request, Forced Browsing型2
CWE-352Cross-Site Request Forgery型3
CWE-1275Sensitive Cookie with Improper SameSite Attribute型3
CWE-918Server-Side Request Forgery型4
CWE-22Path Traversalファイル名を受け取る機能
CWE-601URL Redirection to Untrusted Siteログイン後の戻り先
CWE-548Exposure of Information Through Directory Listing置き忘れ

参照

  1. A01:2025 Broken Access Control OWASP Top 10:2025
  2. Authorization Cheat Sheet OWASP Cheat Sheet Series
  3. Insecure Direct Object Reference Prevention Cheat Sheet OWASP Cheat Sheet Series
  4. Cross-Site Request Forgery Prevention Cheat Sheet OWASP Cheat Sheet Series
  5. Server-Side Request Forgery Prevention Cheat Sheet OWASP Cheat Sheet Series
  6. Authorization Testing OWASP Web Security Testing Guide
  7. V8 Authorization OWASP Application Security Verification Standard 5.0
  8. 安全なウェブサイトの作り方 改訂第7版 IPA 情報処理推進機構
  9. Common Weakness Enumeration MITRE
  10. Fetch Metadata Request Headers W3C
  11. Use the Instance Metadata Service to access instance metadata Amazon Web Services