認証と認可の違い
認証(AuthN)は「あなたは誰か」、認可(AuthZ)は「あなたは何をしてよいか」。順番も役割も別物で、混同すると「ログインさえすれば全部見える」事故になる。
- 1.認証(Authentication)=本人確認「あなたは誰か」、認可(Authorization)=権限判定「あなたは何ができるか」。まず認証、次に認可の順で、別々に判断する。
- 2.混同が事故の元。ログイン済みだから OK とリソースを返すと、他人の ID を指定して他人のデータが見える(IDOR / 権限昇格)。認可は「リソースごとに」毎回確認する。
- 3.未ログインは 401(Unauthorized=認証不足)、ログイン済みでも権限なしは 403(Forbidden=認可で却下)。権限管理は RBAC(役割で付与)か ABAC(属性で判定)で組む。
なぜ分けて考えるのか
英語の頭がどちらも "Auth" で始まるせいで混同されがちですが、役割はまったく別です。
- 認証(AuthN):通信してきた相手が本当に本人かを確かめる。ID とパスワード、ワンタイムコード、生体、OIDC などで「この人は確かに
user123だ」と特定する。詳しい仕組みは Web 認証(Cookie・セッション・JWT)。 - 認可(AuthZ):本人だと分かった上で、この操作・このデータへのアクセスを許してよいかを判断する。「
user123は自分の注文は見られるが、他人の注文や管理画面は見られない」を決める。
ここで決定的に大事なのは、認証が通っても認可が自動的に OK になるわけではないということ。ログインは「建物に入れた」だけで、「どの部屋に入れるか」は別問題です。この一段を省くのが、後述の最も多い事故につながります。
コードやドキュメントで auth とだけ書くと、認証と認可のどちらを指すのか曖昧になります。**AuthN(認証)/ AuthZ(認可)**と書き分けると、設計レビューやレイヤ分割の議論がぶれません。
認証 vs 認可:何が違うか
2つの違いを、観点ごとに並べます。問いの形にすると区別しやすくなります。
| 観点 | 認証(AuthN) | 認可(AuthZ) |
|---|---|---|
| 問い | あなたは誰か? | あなたは何をしてよいか? |
| 確かめる対象 | 本人かどうか(身元) | 権限があるか(操作・リソース) |
| 手段の例 | パスワード / MFA / 生体 / OIDC | RBAC / ABAC / ACL / ポリシー |
| タイミング | 最初に1回(ログイン時) | リクエストごと・リソースごとに毎回 |
| 失敗時のHTTP | 401 Unauthorized | 403 Forbidden |
| ひとことで | 入口で本人確認 | 中で立入許可を判定 |
「タイミング」の行が実装上の肝です。認証はログイン時にまとめて済ませますが、認可はリクエストのたび、しかも触ろうとしているリソース1件ごとに判定し直す必要があります。
一番多い事故:認可の確認漏れ(IDOR)
OWASP Top 10 で**第1位がアクセス制御の不備(Broken Access Control)**です。その典型が IDOR(Insecure Direct Object Reference=直接オブジェクト参照の不備)。「ログインさえしていればOK」として、そのデータが本当にその人のものかを確認し忘れると起きます。
// ❌ 脆弱:認証は見ているが、認可(持ち主チェック)が抜けている
app.get('/orders/:id', requireLogin, async (req, res) => {
// ログイン済みなのは確認済み。だが「誰の注文か」を問わずに返してしまう
const order = await db.orders.findById(req.params.id);
res.json(order); // /orders/1002 と URL を書き換えれば他人の注文が丸見え
});
ログインユーザーが URL の :id を 1001 → 1002 と変えるだけで、他人の注文が見えてしまいます。認証は通っているのに認可をしていない、まさに混同の事故です。直し方は、取得したリソースの所有者と、認証で確定した本人が一致するかを必ず照合すること。
// ✅ 安全:認証で確定した本人と、リソースの持ち主が一致するか検証する
app.get('/orders/:id', requireLogin, async (req, res) => {
const order = await db.orders.findById(req.params.id);
if (!order) return res.sendStatus(404);
// req.user.id は「認証」で確定済みの本人。これと突き合わせるのが「認可」
if (order.userId !== req.user.id) {
return res.sendStatus(403); // 本人だが、このリソースの権限はない
}
res.json(order);
});
認可で最も多い欠陥は、リソースの持ち主・権限を確認せず、ログインの有無だけで通してしまうことです。/users/123/settings のような ID 入りの URL、?role=admin のようなパラメータ、隠してあるだけの管理用エンドポイントは、いずれもサーバ側で毎回権限を再判定しなければ突破されます。UI でボタンを隠しても、攻撃者は直接 API を叩くのでフロントの非表示はアクセス制御ではありません。判定は必ずサーバ側で行ってください。
401 と 403:どちらを返すか
混同しやすいのが 401 と 403 です。認証で落ちたのか、認可で落ちたのかで使い分けます。
| 状況 | ステータス | 意味 | クライアントがすべきこと |
|---|---|---|---|
| ログインしていない / トークン無効・期限切れ | 401 Unauthorized | 認証が不足(あなたが誰か分からない) | ログイン・再認証する |
| ログイン済みだが権限がない | 403 Forbidden | 認可で却下(誰かは分かるが許可がない) | 再ログインしても無駄(権限が要る) |
紛らわしいことに、401 の名前は "Unauthorized" ですが**意味は「未認証(Unauthenticated)」**です。「認証して出直して」が 401、「あなたが誰かは分かっているが、ここは立入禁止」が 403。この区別が HTTP のステータスコード の理解とも噛み合います。
他人のリソースに 403 を返すと、「その ID は存在する」と攻撃者に教えてしまう(リソース列挙のヒントになる)ことがあります。秘匿性が高い場面では、あえて 404 Not Found を返して存在自体を伏せる設計も有効です。「権限が無いことを正直に伝える(403)」か「存在を隠す(404)」かは、情報漏洩リスクとUXのトレードオフで選びます。
認可の設計:RBAC と ABAC
「誰が何をしてよいか」をどうルール化するかが認可の設計です。代表的なのが RBAC と ABAC の2つ。
- RBAC(Role-Based Access Control=役割ベース):ユーザーに「管理者」「編集者」「閲覧者」などの**役割(ロール)**を割り当て、役割に紐づく権限でアクセスを決める。「編集者は記事を書ける/閲覧者は読めるだけ」のように、役割の数だけルールを書けばよく、管理がシンプル。多くのアプリの第一選択。
- ABAC(Attribute-Based Access Control=属性ベース):ユーザー・リソース・状況の属性(部署・所有者・時刻・IP など)を組み合わせたポリシーで判定する。「所有者本人なら編集可」「営業時間内かつ社内ネットワークからのみ可」のような、役割だけでは表せない細かい条件を扱える。
| 観点 | RBAC(役割ベース) | ABAC(属性ベース) |
|---|---|---|
| 判定の軸 | 役割(ロール) | 属性の組み合わせ(誰・何・いつ・どこ) |
| ルール例 | admin は削除可、viewer は閲覧のみ | 所有者かつ営業時間内なら編集可 |
| きめ細かさ | 粗い〜中(役割単位) | 細かい(文脈・条件で動的) |
| 管理のしやすさ | シンプルで運用しやすい | 柔軟だがポリシーが複雑化しやすい |
| 向いている場面 | 多くの業務アプリ・SaaS の基本 | 規制・マルチテナント・きめ細かい制御 |
実務ではまず RBAC を土台にし、役割だけでは表せない条件(「自分のデータだけ」など)を属性で補うハイブリッドが現実的です。先ほどの注文の例(order.userId === req.user.id)も、ロールに加えて所有者という属性で絞る ABAC 的な判定でした。
役割や権限を設計するときは、**最小権限の原則(必要な権限だけを、必要な範囲で与える)を基準にします。「とりあえず admin」を配りすぎると、1アカウントの漏洩が全体の侵害に直結します。デフォルトは拒否(許可したものだけ通す allow-list)**にし、権限は足りなくなってから足すのが安全側です。考え方は 最小権限の原則 も参照。
まとめ:混同しないためのチェックリスト
最後に、設計・レビューで自問すべき点を整理します。
| 問い | 見るべきこと | ミスると起きること |
|---|---|---|
| 相手は本人か?(認証) | トークン/セッションの検証・有効期限 | なりすまし・不正ログイン |
| このリソースを触ってよいか?(認可) | 所有者/ロール/属性をサーバ側で毎回判定 | IDOR・権限昇格(他人のデータ閲覧) |
| 失敗時に正しく返したか? | 401=未認証 / 403=権限なし(必要なら404) | 情報漏洩・クライアント側の誤動作 |
認証と認可は、「誰か」を確かめる工程と「何ができるか」を決める工程という別々のレイヤです。認証が完璧でも認可が抜ければ穴になり、その逆も成り立ちません。具体的な方式の比較は 認証・認可の方式 に、ログイン状態の保持の仕組みは Web 認証 にまとまっています。アクセス制御を「リソースごと・サーバ側・デフォルト拒否」で組む——これが事故を防ぐ一番の近道です。
セキュリティ Article
認証と認可の違いを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
認証
比較で見る軸
難易度: intermediate / カテゴリ: セキュリティ / タグ数: 4
導入後に効く点
混同が事故の元。ログイン済みだから OK とリソースを返すと、他人の ID を指定して他人のデータが見える(IDOR / 権限昇格)。認可は「リソースごとに」毎回確認する。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- intermediate
- カテゴリ
- セキュリティ
- タグ数
- 4
判断チェックリスト
- 自社の用途が「認証 / 認可」に近いか確認する。
- 強みである「認証(Authentication)=本人確認「あなたは誰か」、認可(Authorization)=権限判定「あなたは何ができるか」。まず認証、次に認可の順で、別々に判断する。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。