はじめに
こんにちは。ソーイ株式会社の工藤です。
業務でPostmanを使い、APIの認可チェックを実施する機会がありました。今回は、開発・保守業務におけるAPIのアクセス制御の確認をきっかけに、認証と認可の基本を整理しました。
APIにリクエストを送信し、返ってきたHTTPステータスコードを確認する中で、それぞれの数字が何を意味しているのか、改めて理解を深めたいと思いました。
特に整理したかったのが、401 Unauthorizedと403 Forbiddenの違いです。
結論からいうと、401は有効な認証情報がない場合、403はサーバーがリクエストを理解しているものの、その操作を許可しない場合に返されるステータスコードです。
今回は、HTTPステータスコードの基本を整理したうえで、Node.jsで作成した検証用APIにPostmanからリクエストを送信し、次の3パターンを比較しました。
- 認証情報なし:
401 Unauthorized - 自組織のファイルにアクセス:
200 OK - 他組織のファイルにアクセス:
403 Forbidden
認証情報の有無だけでなく、アクセス先のファイルが所属組織のものかどうかによって結果が変わることを確認し、認証と認可の違いを整理します。
この記事は、APIテストを始めたばかりの方や、401と403の違いを整理したい方に向けた内容です。
HTTPステータスコードとは?
HTTPステータスコードは、クライアントからのリクエストに対して、サーバーが処理結果を伝えるための3桁の数字です。
先頭の数字によって、以下の5つに分類されます。
| 分類 | 意味 | 代表例 |
|---|---|---|
| 1xx | 情報レスポンス | 100 Continue |
| 2xx | 成功 | 200 OK |
| 3xx | リダイレクト | 301 Moved Permanently |
| 4xx | クライアントエラー | 400、401、403、404 |
| 5xx | サーバーエラー | 500、503 |
例えば、APIへのリクエストが成功した場合は200 OK、リクエストに問題がある場合は400 Bad Requestなどが返されます。
ただし、同じステータスコードでも原因は複数考えられるため、数字だけで判断しないことが重要です。
APIテストでよく見るステータスコード一覧
PostmanでAPIのレスポンスを確認する際に役立つよう、代表的なステータスコードと確認ポイントをまとめました。
| コード | 意味 | 最初に確認すること |
|---|---|---|
| 200 OK | リクエストが成功 | 返却データが期待どおりか |
| 201 Created | リソースの作成に成功 | データが正しく作成されたか |
| 204 No Content | 成功したがレスポンス本文はない | 処理結果が期待どおりか |
| 400 Bad Request | リクエストに問題がある | パラメータや送信形式は正しいか |
| 401 Unauthorized | 有効な認証情報がない | トークンの有無や有効期限は正しいか |
| 403 Forbidden | リクエストされた操作が許可されていない | 操作に必要な権限があるか |
| 404 Not Found | リソースが見つからない | URLやリソースIDは正しいか |
| 405 Method Not Allowed | HTTPメソッドが許可されていない | GET・POSTなどの指定は正しいか |
| 422 Unprocessable Content | リクエスト内容を処理できない | 入力値やバリデーション条件は正しいか |
| 429 Too Many Requests | リクエスト回数が制限を超えた | APIのレート制限を超えていないか |
| 500 Internal Server Error | サーバー内部でエラーが発生 | サーバーログにエラーがないか |
| 503 Service Unavailable | サーバーが一時的に利用できない | メンテナンスや障害が発生していないか |
※実際のステータスコードはAPIの仕様や実装によって異なります。
ステータスコードは原因を調べるための手がかりです。想定外の結果が返ってきた場合は、レスポンス本文やAPIの仕様もあわせて確認する必要があります。
認証と認可は何のために必要なのか?
認証と認可は、システムやデータへの不正なアクセスを防ぐための重要な仕組みです。
認証(Authentication) は、アクセスしてきたユーザーが誰なのかを確認する仕組みです。第三者によるなりすましを防ぐために使われます。
一方、認可(Authorization) は、ユーザーなどに対して、許可された範囲の操作だけを行えるようにする仕組みです。
例えば、会社のファイル管理システムでは、ログインできたとしても、ほかの組織のファイルを自由に閲覧できてはいけません。
この場合、認証によってユーザーを確認し、認可によってアクセスできるファイルを制限します。
認証と認可の流れ
※理解しやすいように簡略化した図です。実際には認証前に403を返す場合や、認可拒否時に404を返す場合もあります。
つまり、認証は「誰なのか」、認可は「何をしてよいのか」を判断する仕組みです。
特に整理したかった401と403の違い
401 Unauthorized
401 Unauthorizedは、リクエストに有効な認証情報がない場合に返されるステータスコードです。
例えば、認証が必要なAPIに対して、アクセストークンを付けずにアクセスした場合などです。
HTTP認証では、401のレスポンスにWWW-Authenticateヘッダーが含まれ、必要な認証方式が示されます。
403 Forbidden
403 Forbiddenは、サーバーがリクエストを理解しているものの、その操作を許可しない場合に返されるステータスコードです。
例えば、ログイン済みのユーザーが、アクセス権限のない組織のファイルを取得しようとした場合などです。
ただし、403は必ずしも認証済みのユーザーだけに返されるわけではありません。
Postmanで401・200・403を再現してみた
401と403の違いを実際のレスポンスでも確認するため、Node.jsで簡易的な検証用APIを作成し、Postmanからリクエストを送信しました。
Postmanは、HTTPリクエストを送信し、APIのレスポンスを確認できるツールです。
今回は、同じファイル取得APIに対して、認証情報の有無とアクセス先のファイルIDを変え、401・200・403の3パターンを比較しました。
HTTPメソッドはGETで統一し、認証情報がない場合、自組織のファイルにアクセスする場合、他組織のファイルにアクセスする場合の違いを確認します。
これにより、認証だけでなく、所属組織に基づく認可判定の動作も確認できます。
① 検証用APIの準備
今回は、Node.jsでローカルに検証用APIを起動し、Postmanからリクエストを送信しました。
検証環境は以下のとおりです。
- Node.js:v20.19.3(標準の
httpモジュールを使用) - Postman:v12.33.3(Mac版)
- APIの実行先:
localhost:4500
PostmanのMock Serverではなく、Node.jsでAPIを動かす方法を選んだのは、認証だけでなく、ユーザーの所属組織とファイルの所属組織を比較する認可判定も確認したかったためです。
以下のコードをserver.jsとして保存します。
const http = require("http");
const PORT = process.env.PORT || 4500;
// 検証用ユーザー
const USERS = {
"demo-org-a-token": {
userId: "user-a",
organizationId: "org-a"
}
};
// 検証用ファイル
const FILES = {
"file-a": {
id: "file-a",
organizationId: "org-a",
name: "sample-a.txt"
},
"file-b": {
id: "file-b",
organizationId: "org-b",
name: "sample-b.txt"
}
};
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
const url = new URL(req.url, `http://localhost:${PORT}`);
const match = url.pathname.match(/^\/api\/demo\/files\/([^/]+)$/);
if (req.method !== "GET" || !match) {
res.writeHead(404);
return res.end(JSON.stringify({ error: "Not Found" }));
}
// ① 認証:トークンからユーザーを特定
const auth = req.headers.authorization;
const token = auth?.startsWith("Bearer ")
? auth.slice(7)
: null;
const user = USERS[token];
if (!user) {
res.writeHead(401, {
"WWW-Authenticate": "Bearer"
});
return res.end(JSON.stringify({
error: "Unauthorized",
message: "Valid authentication is required."
}));
}
const file = FILES[match[1]];
if (!file) {
res.writeHead(404);
return res.end(JSON.stringify({ error: "Not Found" }));
}
// ② 認可:ユーザーとファイルの所属組織を比較
if (user.organizationId !== file.organizationId) {
res.writeHead(403);
return res.end(JSON.stringify({
error: "Forbidden",
message: "You do not have permission to access this file."
}));
}
// ③ 認証・認可の両方に成功
res.writeHead(200);
return res.end(JSON.stringify(file));
});
server.listen(PORT, () => {
console.log(`API server running at http://localhost:${PORT}`);
});
ターミナルで以下を実行すると、APIが起動します。
node server.js
起動すると、次のメッセージが表示されます。
API server running at http://localhost:4500
このコードでは、トークンからユーザーを特定したあと、ユーザーとファイルのorganizationIdを比較してアクセス可否を判定します。
なお、トークンとファイル情報はすべて検証用のダミーデータです。実際の認証基盤やデータベースは使用していません。
② 認証情報なし:401 Unauthorized
まず、認証情報を付けずに、組織Aのファイルへアクセスします。
GET http://localhost:4500/api/demo/files/file-a
PostmanのAuthorizationをNo Authに設定して送信しました。
結果は401 Unauthorizedでした。
{
"error": "Unauthorized",
"message": "Valid authentication is required."
}
トークンがないため、サーバーはユーザーを特定できず、401を返しています。
③ 自組織のファイル:200 OK
次に、組織Aのユーザーとして、組織Aのファイルにアクセスします。
URLはそのままで、AuthorizationをBearer Tokenに変更し、検証用トークンdemo-org-a-tokenを設定しました。
GET http://localhost:4500/api/demo/files/file-a
結果は200 OKでした。
{
"id": "file-a",
"name": "sample-a.txt",
"organizationId": "org-a"
}
トークンから組織Aのユーザーを特定でき、ファイルの所属組織も一致しているため、アクセスが許可されました。
④ 他組織のファイル:403 Forbidden
最後に、同じ組織Aのトークンを使用したまま、組織Bのファイルにアクセスします。
変更するのはURLのファイルIDだけです。
GET http://localhost:4500/api/demo/files/file-b
結果は403 Forbiddenでした。
{
"error": "Forbidden",
"message": "You do not have permission to access this file."
}
トークンは有効ですが、ユーザーの所属組織org-aとファイルの所属組織org-bが一致しないため、認可によってアクセスが拒否されました。
⑤ 検証結果の比較
| 検証条件 | 認証 | 認可 | 結果 |
|---|---|---|---|
| トークンなし → file-a | 失敗 | 判定しない | 401 Unauthorized |
| 組織Aのトークン → file-a | 成功 | 許可 | 200 OK |
| 組織Aのトークン → file-b | 成功 | 拒否 | 403 Forbidden |
今回の検証では、認証と認可を別々に判定することで、3つのステータスコードの違いを確認できました。
特に、同じトークンでもアクセス先のファイルによって200と403が変わることから、認可が実際に機能していることを確認できました。
再現時の注意点
今回のAPIは、トークンの文字列を比較するだけの学習用実装です。実際の認証基盤やデータベースは使用していません。
また、403は必ずしも認証済みユーザーにだけ返されるものではありません。今回は、認証成功後に所属組織を比較する設計にしたため、認可拒否時に403が返されています。
実際のAPIでは、ステータスコードだけでなく、サーバー側の判定処理やAPIの仕様も確認する必要があります。
実際のAPI認可チェックで学んだこと
業務では、Postmanを使って、他組織のファイルにアクセスできないことを確認するAPIテストを実施しました。
具体的には、組織Aに所属するユーザーの認証情報を使い、組織Bのファイルにアクセスするリクエストを送信しました。
実際に確認した内容
以下は、業務での検証内容を説明用に簡略化したものです。
| 検証内容 | 期待結果 | 実際の結果 |
|---|---|---|
| 組織Bのファイル一覧を組織Aのユーザーで取得する | アクセス拒否 | 403 Forbidden |
| 組織Bのファイルのダウンロード用URLを組織Aのユーザーで発行する | アクセス拒否 | 403 Forbidden |
どちらも403が返り、今回の検証条件では他組織のファイルへのアクセスが拒否されることを確認できました。
今回の検証では、認証済みのユーザーであっても、所属していない組織のデータにはアクセスできないことが重要な確認ポイントでした。
認可テストで確認しておきたい観点
今回確認した内容に加えて、認可テストでは次のような観点も重要だと考えました。
| 確認観点 | テスト例 | 確認したいこと |
|---|---|---|
| リソースIDの変更 | URLやリクエスト内のIDを他組織のものに変更する | IDを知っているだけで他組織のデータにアクセスできないか |
| HTTPメソッドの違い | GETだけでなく、POST・PUT・DELETEなども確認する | 取得以外の操作でも認可が適切に行われるか |
| ダウンロードURLの扱い | 発行済みURLを別組織のユーザーが開く | URLの有効期限や共有範囲が仕様どおりか |
| 認証情報の変更 | トークンなし・無効なトークンでアクセスする | 認証されていない状態でアクセスできないか |
※上記は今後確認すべきテスト観点を整理したものであり、すべてを今回の業務で検証したわけではありません。
特に、ダウンロード用URLは、署名付きURLなどの仕組みによっては、URLを知っている人が有効期限内にアクセスできる設計もあります。そのため、単に別組織のユーザーが開けるかだけでなく、そのURLがどのような条件で利用できる仕様なのかを確認する必要があります。
また、APIによっては、アクセス権限がない場合に403ではなく404を返す設計があります。
403を返すと、アクセス権限はなくても「そのリソースが存在する」という情報を相手に与える可能性があります。そのため、リソースの存在自体を知られないように、あえて404を返す場合があります。
例えば、GitHubでは、アクセス権限のないプライベートリポジトリにアクセスした際、リポジトリの存在を隠すために404が返されます。
そのため、403が返ったことだけで、すべての認可制御が正しく実装されているとは判断できません。
今回の経験から、APIテストではステータスコードだけを見るのではなく、誰が、どのリソースに対して、どの操作を行ったのかを整理し、APIの仕様と照らし合わせることが重要だと学びました。
まとめ
今回は、業務でのAPI認可チェックをきっかけに、HTTPステータスコードの基本と認証・認可の違いについて整理しました。
Node.jsで作成した検証用APIにPostmanからリクエストを送信し、次の3パターンを確認しました。
| 検証条件 | 結果 |
|---|---|
| 認証情報なしでアクセス | 401 Unauthorized |
| 組織Aのユーザーが自組織のファイルにアクセス | 200 OK |
| 組織Aのユーザーが他組織のファイルにアクセス | 403 Forbidden |
今回の検証で特に理解が深まったのは、認証に成功していても、必ずしもすべてのリソースにアクセスできるわけではないという点です。
同じ認証情報を使用していても、アクセス先のファイルが所属組織のものかどうかによって、200と403に結果が分かれました。
また、業務でのAPIテストを通して、認可の確認では次の3点が重要だと学びました。
- 誰が:どのユーザー・組織の認証情報を使用しているか
- 何に対して:どの組織・リソースにアクセスしようとしているか
- 何をするか:取得・作成・更新・削除など、どの操作を行うか
HTTPステータスコードは、APIの動作を理解するための手がかりです。ただし、ステータスコードだけで認可の正しさを判断することはできません。
今後もAPIテストでは、返ってきた数字だけでなく、どのような条件でアクセスが許可・拒否されたのかまで確認していきたいと思います。
参考資料
- MDN Web Docs - HTTP レスポンスステータスコード
- MDN Web Docs - 401 Unauthorized
- MDN Web Docs - 403 Forbidden
- Postman公式ドキュメント
お知らせ
技術ブログを週1〜2本更新中、ソーイをフォローして最新記事をチェック!
https://qiita.com/organizations/sewii


