authentication-failures について
Authentication Failuresは、OWASP A07:2025に基づき、FastAPIとFlaskアプリの認証を点検するセキュリティスキルです。テスト用チェックリスト、スキャンコマンド、一般的な認証・セッションリスクの修正優先度を提示します。
主な機能
- 01JWT設定とクレーム検証を点検
- 02認証エンドポイントのレート制限を確認
- 03パスワードポリシーと漏えいパスワード照合を確認
- 04セッション管理とエラーメッセージを点検
- 05スキャンコマンドと修正優先度を提示
活用シーン
- 01FastAPIまたはFlaskのログインフローを点検
- 02JWT検証とセッション無効化の設定を確認
- 03ハードコードされた認証情報や弱いパスワード方針を発見
- 04認証リスクの修正を優先順位付け
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ scholarly360/ owasp-top10-web-skills --skill authentication-failuresインストール後、Claude Code で/authentication-failuresを入力して読み込まれたか確認します。
詳細
認証の失敗 — OWASP A07:2025
順位: #7(変動なし) | 対象となる CWE: 36 | 発生件数: 110 万件以上
2025 年の新たな脅威: ハイブリッドパスワード攻撃/パスワードスプレー — 攻撃者は漏えいした認証情報を少しずつ変更して試します(例:Password1! → Password2!)。
このスキルで扱う内容
認証の失敗は、アプリケーションの本人確認メカニズムが存在しない、脆弱である、または回避可能な場合に発生します。主な攻撃パターン:
- クレデンシャルスタッフィング/ブルートフォース攻撃 — 漏えいした認証情報を使った自動ログイン試行
- 脆弱なパスワード/デフォルトパスワード — 推測されやすい、または漏えいが確認されているパスワードで作成されたアカウント
- 安全でない認証情報の復旧 — 情報が漏えいする、または回避可能なパスワードリセットフロー
- MFA の欠如または回避 — 第 2 要素がない、またはスキップできる MFA
- セッション識別子の露出 — URL、ログ、またはリファラーヘッダー内のトークン
- セッション固定 — 認証前に攻撃者が既知のセッション ID を設定する
- 不適切なセッション無効化 — ログアウト後も存続する、またはトークン失効後も有効なセッション
FastAPI と Flask のテストチェックリスト
各セクションを順に確認してください。合格は ✓、不合格は ✗、該当しない場合は N/A を記入します。
1. JWT の実装
# ✓ GOOD — short-lived tokens, claim validation
token = jwt.encode(
{"sub": user_id, "exp": datetime.utcnow() + timedelta(minutes=15),
"iss": "myapp", "aud": "myapp-client"},
settings.SECRET_KEY, algorithm="HS256"
)
jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"],
audience="myapp-client", issuer="myapp")
# ✗ BAD — no expiry, no claim validation, accepts any algorithm
jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256", "RS256", "none"])
チェック項目:
- アクセストークンの有効期限が 15~30 分
- リフレッシュトークンは使用時にローテーションされ、ログアウト時に無効化される
-
aud(対象者)とiss(発行者)のクレームが設定され、デコード時に検証される - アルゴリズムは明示的な許可リストに固定されている —
"none"は絶対に含めない - JWT シークレットは
os.environから読み込み、長さは 32 文字以上 - アルゴリズム混同攻撃を防止している:RS256 のキーが HS256 のシークレットとして受け入れられない
2. 認証エンドポイントのレート制限
# FastAPI — slowapi
from slowapi import Limiter
from slowapi.util import get_remote_address
limiter = Limiter(key_func=get_remote_address)
@app.post("/login")
@limiter.limit("5/minute") # ✓ per-IP rate limit
async def login(request: Request, credentials: LoginSchema):
...
# Flask — Flask-Limiter
from flask_limiter import Limiter
limiter = Limiter(app, key_func=get_remote_address, default_limits=["200/day"])
@app.route("/login", methods=["POST"])
@limiter.limit("5 per minute") # ✓
def login():
...
チェック項目:
- ログインエンドポイントにレート制限が設定されている(IP ごとに 1 分あたり 5~10 回以下)
- 失敗が繰り返された場合、アカウント単位でロックアウトする(IP 単位だけではない)
- 登録およびパスワードリセットのエンドポイントにもレート制限が設定されている
- レート制限への応答は
401ではなく429 Too Many Requestsを返す
3. パスワードポリシーと漏えいパスワードの確認
# ✓ GOOD — check against breached databases
import hashlib, httpx
def is_pwned(password: str) -> bool:
sha1 = hashlib.sha1(password.encode()).hexdigest().upper()
prefix, suffix = sha1[:5], sha1[5:]
resp = httpx.get(f"https://api.pwnedpasswords.com/range/{prefix}")
return suffix in resp.text
# ✓ GOOD — enforce minimum complexity
def validate_password(password: str):
if len(password) < 12:
raise ValueError("Password must be at least 12 characters")
if is_pwned(password):
raise ValueError("Password found in known data breach")
チェック項目:
- 最小文字数が適用されている(NIST 800-63b では 8 文字以上を推奨。12 文字以上が望ましい)
- 既知の漏えいパスワードと照合する(HaveIBeenPwned API またはローカルリスト)
- 定期的な変更を強制しない(NIST 800-63b では廃止されています)
- 最悪のパスワード上位 10,000 件のリストと照合する
- パスワードの最大長が 64 文字未満に設定されていない
4. セッション管理(Flask)
# ✓ GOOD — Flask-Login with secure session config
app.config.update(
SESSION_COOKIE_SECURE=True, # HTTPS only
SESSION_COOKIE_HTTPONLY=True, # no JS access
SESSION_COOKIE_SAMESITE='Lax', # CSRF mitigation
PERMANENT_SESSION_LIFETIME=timedelta(hours=1),
)
@app.route("/logout")
@login_required
def logout():
logout_user() # ✓ invalidates session
session.clear() # ✓ belt-and-suspenders
return redirect("/login")
チェック項目:
-
Flask-LoginまたはFlask-Security-Tooを使用している(独自のセッション管理を実装していない) - ログアウト時にセッションが無効化される(サーバー側での無効化と Cookie の消去の両方)
- ログイン成功後にセッション ID が再生成される(セッション固定を防止)
-
SESSION_COOKIE_SECURE、SESSION_COOKIE_HTTPONLY、SESSION_COOKIE_SAMESITEがすべて設定されている - セッションの有効期間が制限されており、アイドル状態のセッションは期限切れになる
5. エラーメッセージによるアカウント列挙
# ✗ BAD — reveals which field was wrong
if not user:
return {"error": "Username not found"}
if not verify_password(password, user.hashed_password):
return {"error": "Incorrect password"}
# ✓ GOOD — generic message regardless of failure reason
if not user or not verify_password(password, user.hashed_password):
return JSONResponse({"error": "Invalid credentials"}, status_code=401)
チェック項目:
- 認証失敗時は常に同一のメッセージ(
"Invalid credentials"または同等のメッセージ)を返す - HTTP ステータスコードに一貫性がある:ユーザーが存在しない場合に
404、パスワードが誤っている場合に401を返すようなことはしない - パスワードリセットフローで、メールアドレスが登録済みかどうかを確認できない
- 応答時間が一定である(
hmac.compare_digestを使い、ユーザーが不明な場合でも必ずハッシュ化する)
6. デフォルト認証情報とハードコードされたシークレット
# ✗ BAD — Bandit will flag these
ADMIN_PASSWORD = "admin"
TEST_USER = {"username": "test", "password": "test123"}
JWT_SECRET = "supersecretkey"
# ✓ GOOD
JWT_SECRET = os.environ["JWT_SECRET"] # loaded from environment / secrets manager
チェック項目:
- Bandit を実行する:
bandit -r . -t B105,B106,B107(ハードコードされたパスワードの確認) - 設定、シードデータ、フィクスチャに
admin/admin、test/test、root/rootのようなパターンがない - すべてのシークレットを環境変数またはシークレットマネージャー(Vault、AWS SSM)から読み込む
-
.envファイルが.gitignoreによってバージョン管理から除外されている
7. MFA の適用
チェック項目:
- すべてのユーザーアカウントで MFA を利用でき、管理者/特権ロールでは必須になっている
- URL パラメーターやセッション状態を操作しても MFA を回避できない
- TOTP コードは 1 回のみ使用でき、有効期間は 30 秒(標準的な TOTP)
- リカバリーコードは 1 回のみ使用でき、ハッシュ化した値として保存される
主要な CWE のリファレンス
| CWE | 名前 | 典型的な検出事項 |
|---|---|---|
| CWE-287 | 不適切な認証 | 認証チェックがない、または認証を回避できる |
| CWE-307 | 認証試行の制限なし | ログインエンドポイントにレート制限がない |
| CWE-384 | セッション固定 | ログイン後にセッション ID が再生成されない |
| CWE-521 | 脆弱なパスワード要件 | 最小の長さや複雑さが適用されていない |
| CWE-798 | ハードコードされた認証情報 | ソースコードにシークレットが含まれている |
| CWE-613 | セッションの有効期限が不十分 | ログアウト後もセッションが有効なまま、タイムアウトがない |
自動スキャンコマンド
# Static analysis — catches CWE-798, weak crypto, debug mode
bandit -r . -ll -ii
# Specifically target hardcoded credentials
bandit -r . -t B105,B106,B107,B108
# Dependency vulnerabilities (may expose auth libraries with known CVEs)
pip-audit
safety check
# Check JWT library versions for known vulns
pip show python-jose PyJWT authlib
修正の優先順位
- 重大: JWT のアルゴリズム混乱/
"none"を許可 → ただちに修正 - 重大: ログインにレート制限がない → ただちに
slowapi/Flask-Limiterを追加 - 高: ソースコードにシークレットがハードコードされている → 環境変数に移行
- 高: ログアウト時にセッションが無効化されない → セッション管理を修正
- 中: エラーメッセージによるユーザー名の列挙 → エラーレスポンスを統一
- 中: 漏えいしたパスワードのチェックがない → HaveIBeenPwned API を統合
- 低: MFA の選択肢がない → まず特権アカウントへの導入を計画
参考資料
- OWASP A07:2025 の完全な仕様:https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/
- NIST SP 800-63b(デジタルアイデンティティガイドライン):https://pages.nist.gov/800-63-3/sp800-63b.html
- HaveIBeenPwned API:https://haveibeenpwned.com/API/v3
コメント
Markdown 対応