Open1

【認証・許可系/Auth系】API認証の設計/実装について📝

まさぴょん🐱まさぴょん🐱

【認証・許可系/Auth系】API認証の設計/実装について📝

https://qiita.com/YushiYamamoto/items/3f28aeaeb099149b561f

「API 認証をどう設計・実装するか」 を目的別に整理し、FastAPI や Node(Hono/Express)での実装例、そして OWASP 最新ガイドラインに基づくベストプラクティスまでまとめます。

1. まず整理すべき4つの観点

観点 具体的に決めること
主体 (Who) 第三者アプリ?自社 SPA?サーバ間バッチ?
方式 (How) API キー / Basic / Bearer(JWT) / OAuth2 / mTLS など
有効期間 (When) 使い捨ての短命トークンか、長期 API キーか
権限範囲 (What) スコープ / RBAC / ABAC / テナント分離方法

2. 代表的な認証方式比較

方式 主なユースケース ステートレス トークン失効の難しさ 実装ライブラリ例
API キー (HMAC でも可) 内部連携・Webhook 検証 FastAPI: Header() / Node: Hono c.req.header()
HTTP Basic 単純な PoC、VPN 内限定 Same as above
Bearer (JWT) SPA ↔ API、モバイルアプリ 高 (要ブラックリスト or 短寿命) FastAPI OAuth2PasswordBearer, PyJWT / Node jsonwebtoken
OAuth2 Client Credentials サーバ間 (Machine-to-Machine) FastAPI Authlib / Node simple-oauth2
OAuth2 Authorization Code + PKCE 外部サードパーティ連携 Same as above
OpenID Connect 「ログインして誰かを識別」 Same as above
Mutual TLS 金融・社内ゼロトラスト Web サーバ設定+CA 運用

3. Bearer(JWT)トークン実装ステップ(FastAPI 例)

from datetime import timedelta, datetime
from jose import jwt, JWTError
from passlib.context import CryptContext
from fastapi import Depends, Header, HTTPException, status

SECRET_KEY = "<<<強力なランダム文字列>>>"
ALGORITHM = "HS256"
ACCESS_LIFETIME = timedelta(minutes=30)

pwd_ctx = CryptContext(schemes=["bcrypt"], deprecated="auto")

def create_access_token(sub: str) -> str:
    exp = datetime.utcnow() + ACCESS_LIFETIME
    to_encode = {"sub": sub, "exp": exp}
    return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload["sub"]
    except JWTError:
        raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED)
  • /auth/token でパスワードを受け取り、create_access_token() を返す
  • 守りたいエンドポイントで Depends(get_current_user) を挟むだけで認証完了
    FastAPI デフォルトのドキュメント UI でも「Authorize」ボタンが自動表示されます (FastAPI, FastAPI)

4. API キーを 1 つのエンドポイントだけに付ける(Node / Hono 例)

import { Hono } from 'hono';

const API_KEY = process.env.FASTAPI_MCP_API_KEY ?? 'secret-fast-api-mcp-token';

const app = new Hono();

// 認証付き
app.get('/protected', (c) => {
  const key = c.req.header('x-api-key');
  if (key !== API_KEY) return c.json({ error: 'Unauthorized' }, 401);
  return c.text('OK!');
});

// 認証不要
app.get('/public', (c) => c.text('Hello, World!'));

export default app;
  • 環境変数にキーを置く
  • 付与したいルートのみヘッダチェック
    (FastAPI でも同様に Depends() で切り替え可能)

5. 実装後に必ず押さえる OWASP API Top 10 (2023) の “Broken Authentication” 対策

  1. 常時 HTTPS(TLS1.2+)
  2. トークンの短寿命化 + Refresh トークン方式
  3. 強力なパスワードハッシュ (bcrypt, argon2)
  4. 多要素認証 (MFA) を UI がある場合は有効化
  5. レート制限・ブルートフォース検知
  6. ログと監査証跡を残す(成功/失敗・トークン失効)
  7. キー/シークレットのローテーション手順を用意
  8. 自動化テストで「認証なしで呼べるエンドポイント」を検知
  9. RBAC/スコープ で最小権限原則を徹底
  10. 外部 SaaS/IAM 連携なら自前実装を最小化して脆弱性を減らす

(詳細は OWASP API Security Top 10 2023 の API2:2023 を参照) (OWASP)


6. 迷ったときの選定ガイド

要件 推奨方式
モバイル・SPA ログイン OAuth2 + JWT(Authorization Code + PKCE)
サーバ間バッチ OAuth2 Client Credentials or mTLS
社内マイクロサービス mTLS + 認可ヘッダ(SPIFFE/SPIRE など)
小規模・社内ツール 単一 API キー or Basic
公開 API / 他社連携 OAuth2 / OpenID Connect(外部 IdP 連携)

まとめ

  1. 認証方式は「誰が・どう使うか」で決める
  2. 実装はライブラリに任せ、脆弱な独自実装は避ける
  3. OWASP API Top 10 を常にチェックし、短寿命トークン+最小権限で運用する

この流れを押さえれば、FastAPI や Hono でも数十行で安全な認証を導入できます。さらに詳しく知りたいフロー(PKCE、mTLS 設定、スコープ設計など)があれば、ぜひ追加で聞いてください。