サイトアイコン IT & ライフハックブログ|学びと実践のためのアイデア集

FastAPIのセキュリティ設計:認証と認可、JWT、CORSの実務ポイント

green snake

Photo by Pixabay on Pexels.com

FastAPIのセキュリティは、一つの機能を追加すれば完成するものではありません。

利用者の本人確認、操作権限、ブラウザからの接続元、リクエスト量、実装上の欠陥は、それぞれ別の仕組みで管理します。

この記事では、認証と認可、JWT、CORS、APIキー、レート制限、脆弱性診断の役割を分け、FastAPIで設計するときの判断順を整理します。

掲載するコードは構造を説明するための最小例です。データベース、秘密情報の保管、鍵の更新、監査ログ、障害時の処理まで含む完成品ではありません。

最初に分けたい六つの対策

似た用語を同じ対策として扱うと、設定を追加しても守れない経路が残ります。

FastAPIで組み合わせる主なセキュリティ対策
対策 確認すること 単独では防げないこと
認証 誰がリクエストしたか その利用者が何を操作できるか
認可 その操作を許可してよいか 利用者本人の確認
CORS どのオリジンのブラウザ画面に応答を読ませるか ブラウザ以外からのAPI呼び出し
APIキー どのクライアントが呼び出したか キーを使う個人の本人確認
レート制限 一定時間に受け付ける量 許可された回数内の不正操作
脆弱性診断 コードや稼働中APIに既知の問題がないか 要件や権限設計そのものの妥当性

この区別を設計書とテスト項目にも反映すると、対策の目的と担当箇所を追いやすくなります。

OAuth2とJWTによる認証

認証は、リクエストを送った主体を確認する処理です。

FastAPIでは、OAuth2のBearerトークンを依存関数で受け取り、署名や有効期限を検証して利用者を特定する構成を作れます。

JWT(JSON Web Token)は、JSON形式の情報に署名を付けて受け渡すための形式です。署名によって改変を検出できますが、内容を暗号化する仕組みではありません。

JWTの構造、保存場所、失効管理は、JWTの仕組みと安全に使うための注意点でも整理しています。

トークン発行と検証の最小構成

次の例は、秘密鍵をソースコードに固定せず、環境変数から読む構成です。

import os
from datetime import datetime, timedelta, timezone

import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import InvalidTokenError

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
SECRET_KEY = os.environ["JWT_SECRET_KEY"]
ALGORITHM = "HS256"

def create_access_token(subject: str, expires_minutes: int = 30) -> str:
    expires_at = datetime.now(timezone.utc) + timedelta(minutes=expires_minutes)
    payload = {"sub": subject, "exp": expires_at}
    return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

def get_current_subject(token: str = Depends(oauth2_scheme)) -> str:
    unauthorized = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="認証情報を確認できません",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        subject = payload.get("sub")
        if not subject:
            raise unauthorized
        return subject
    except InvalidTokenError:
        raise unauthorized

subには利用者を一意に識別できる値を入れ、expには有効期限を入れます。

一方、ログイン処理では保存済みのパスワードハッシュを検証します。平文パスワードをコードやデータベースに保存する構成は避け、利用するJWTライブラリとハッシュライブラリは導入時のFastAPI公式資料に合わせて選びます。

本番実装でコード例に足すもの

認可は認証の後で判定する

認可は、認証済みの主体に特定の操作を許すかを決める処理です。

ログインできたことと、管理者向けのデータを読めることは別の条件です。

ロールベース認可では、管理者、編集者、閲覧者などの役割に許可を割り当て、エンドポイントごとに必要な役割を依存関数で確認します。

  1. トークンを検証して主体を特定する
  2. 信頼できるデータから現在のロールや権限を取得する
  3. 対象エンドポイントに必要な権限と照合する
  4. 認証できない場合は401、権限が足りない場合は403として扱う

権限をトークンに含める場合も、権限変更がいつ反映されるかを決める必要があります。短い有効期限、失効の仕組み、サーバー側での再確認のどれを使うかは、要求される即時性に合わせます。

役割だけでは表現しにくい権限には、OAuth2スコープのような細かな許可単位を検討できます。ただし、権限の種類が少ないAPIへ複雑な仕組みを持ち込むと、設定と確認の負担も増えます。

CORSが制御する範囲

CORS(Cross-Origin Resource Sharing)は、別のオリジンで動くブラウザ上のJavaScriptに、APIの応答を読ませるかを制御する仕組みです。

オリジンは、通信方式、ドメイン、ポートの組み合わせです。ドメインが同じでも、HTTPとHTTPS、またはポートが違えば別のオリジンとして扱われます。

CORSは認証や認可の代わりにはなりません。ブラウザ以外のクライアントはCORSによる制御の対象ではないため、API自体には認証と認可が必要です。

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://your-frontend.example"],
    allow_credentials=True,
    allow_methods=["GET", "POST", "PUT", "DELETE"],
    allow_headers=["Authorization", "Content-Type"],
)

本番環境では、実際に必要なオリジン、メソッド、ヘッダーを列挙します。認証情報を伴う通信では、許可元をワイルドカードにせず明示します。

フロントエンドとのAPI契約や配信方法も含めて設計する場合は、FastAPIとフロントエンド連携の設計ポイントも参照してください。

APIキーとレート制限の役割

APIキーは、連携先のシステムや契約単位など、APIを利用するクライアントを識別するために使います。

利用者個人の本人確認が必要なAPIでは、APIキーだけで済ませず、利用者向けの認証と組み合わせます。

レート制限は、一定時間に受け付けるリクエスト量を制御する仕組みです。

元記事で例示していたslowapiのようなライブラリを使う場合も、IPアドレス、APIキー、利用者のどれを基準に数えるかを先に決めます。

共有回線やプロキシの背後では複数の利用者が同じ送信元に見えることがあるため、IPアドレスだけを基準にすると正当な利用まで制限する可能性があります。

静的解析と動的診断を使い分ける

静的解析はソースコードを調べ、危険な書き方や設定の候補を探します。元記事で挙げたBanditはPythonコードを対象にできます。

動的診断は稼働中のAPIへリクエストを送り、応答や振る舞いを確認します。OWASP ZAPはこの用途で使われます。

両方をCI/CDへ組み込むと変更のたびに確認できますが、検査を通過しても権限設計が正しいとは限りません。

診断結果は、再現条件、影響する機能、修正後の再試験まで記録し、誤検知と未対応を区別します。

本番公開前の確認項目

アプリケーション全体のテスト、監視、運用まで確認するには、FastAPIを本番運用へ進める実務チェックリストも役立ちます。

安全なAPIへつなげる設計順序

FastAPIのAPIを守るには、認証、認可、CORS、APIキー、レート制限、脆弱性診断を目的別に組み合わせます。

まず主体と権限を分け、次にブラウザの接続元と利用量を制御し、最後に静的解析と動的診断で実装を点検します。

サンプルコードをそのまま本番へ移すのではなく、秘密情報の保管、失効、監査、障害時の手順まで設計してから公開してください。

この記事に関連する株式会社greedenの取り組み

認証や権限、CORSを個別に足すだけでは、運用時の抜けを防げません。株式会社greedenは、要件定義からAPI設計、実装、テスト、保守まで一気通貫で支援し、安全性を含むWebシステム開発に伴走します。

モバイルバージョンを終了