mcpField
A
authentication-failures

authentication-failures

github.com/scholarly360セキュリティ2248 回インストール29 週間前に更新
リポジトリ

authentication-failures について

Authentication Failuresは、OWASP A07:2025に基づき、FastAPIとFlaskアプリの認証を点検するセキュリティスキルです。テスト用チェックリスト、スキャンコマンド、一般的な認証・セッションリスクの修正優先度を提示します。

主な機能

  1. 01JWT設定とクレーム検証を点検
  2. 02認証エンドポイントのレート制限を確認
  3. 03パスワードポリシーと漏えいパスワード照合を確認
  4. 04セッション管理とエラーメッセージを点検
  5. 05スキャンコマンドと修正優先度を提示

活用シーン

  1. 01FastAPIまたはFlaskのログインフローを点検
  2. 02JWT検証とセッション無効化の設定を確認
  3. 03ハードコードされた認証情報や弱いパスワード方針を発見
  4. 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

修正の優先順位

  1. 重大: JWT のアルゴリズム混乱/"none" を許可 → ただちに修正
  2. 重大: ログインにレート制限がない → ただちに slowapi/Flask-Limiter を追加
  3. 高: ソースコードにシークレットがハードコードされている → 環境変数に移行
  4. 高: ログアウト時にセッションが無効化されない → セッション管理を修正
  5. 中: エラーメッセージによるユーザー名の列挙 → エラーレスポンスを統一
  6. 中: 漏えいしたパスワードのチェックがない → HaveIBeenPwned API を統合
  7. 低: MFA の選択肢がない → まず特権アカウントへの導入を計画

参考資料

コメント