mcpField
C
cryptographic-failures

cryptographic-failures

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

cryptographic-failures について

Python アプリの暗号化の失敗を調べるセキュリティスキルです。Bandit による静的スキャンと優先度別のコード確認、修正指針を組み合わせます。

主な機能

  1. 01Bandit による暗号問題のスキャン
  2. 02パスワードハッシュと乱数の確認
  3. 03ハードコード鍵と TLS 検証の確認
  4. 04JWT と共通鍵暗号の監査
  5. 05Python フレームワーク別の指針

活用シーン

  1. 01Python コードの弱い暗号化の監査
  2. 02トークンとパスワード処理の確認
  3. 03TLS と証明書検証の点検
  4. 04暗号問題の修正方針の策定

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/scholarly360/owasp-top10-web-skills --skill cryptographic-failures

インストール後、Claude Code で/cryptographic-failuresを入力して読み込まれたか確認します。

同梱ファイルreferences/owasp-a04-full.md

詳細

A04:2025 — 暗号化の失敗

OWASP ランク: #4(TLS の採用が進んだため、#2 から低下) 発生率: アプリケーションの 3.80%、160 万件以上、32 件の CWE

暗号化の失敗とは、保存中および転送中のデータを保護する暗号化の欠如、誤用、または脆弱性を指します。Python には、Java/.NET とは異なる特有の落とし穴があります。


このスキルで扱う内容

領域主なリスクPython 固有の落とし穴
パスワードのハッシュ化脆弱なアルゴリズム、または未使用bcrypt/Argon2 ではなく hashlib.md5/sha1 を使用
乱数予測可能な値import secrets ではなく import random を使用
ハードコードされたキーキーの漏えいソースコード内に SECRET_KEY = "..." のリテラルがある
TLS/SSL証明書の検証回避requests/httpx で verify=False を指定
JWT シークレットキーの強度、アルゴリズム脆弱なシークレット、アルゴリズムの固定なし
共通鍵暗号非推奨のモードECB モード、認証付き暗号化なし

監査の手順

1. Bandit による静的スキャン

暗号化の問題を自動的に検出する、Python 向けの主要な SAST ツールである Bandit を実行します。

pip install bandit
bandit -r . -t B303,B304,B305,B306,B311,B324,B501,B502,B503,B504,B505,B506 -f json -o bandit_crypto.json

暗号化の失敗に関連する主な Bandit テスト ID:

Bandit ID検出対象深刻度
B303MD5/SHA1 の使用中
B304/B305脆弱な暗号モード(ECB、DES)高
B311セキュリティ関連の処理での random モジュール低
B324hashlib での脆弱なハッシュの使用中
B501SSL コンテキストでの verify=False高
B502/B503/B504安全でない SSL/TLS バージョン中〜高
B105/B106ハードコードされたパスワード/シークレット中
2. コードパターンの手動レビュー

以下のパターンを優先度の高い順に確認します。

🔴 重大 — パスワードのハッシュ化
# BAD — MD5/SHA1 for passwords: CWE-328, CWE-759, CWE-916
hashlib.md5(password.encode()).hexdigest()
hashlib.sha1(password.encode()).hexdigest()
hashlib.sha256(password.encode()).hexdigest()  # also bad — no salt, no stretching

# GOOD — use passlib with bcrypt or argon2
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")
# OR
pwd_context = CryptContext(schemes=["bcrypt"], bcrypt__rounds=12, deprecated="auto")
hashed = pwd_context.hash(plain_password)

確認項目:

  • パスワードまたはトークンのハッシュ化に hashlib.* の呼び出しを使用している箇所
  • ソルトがない(hashlib.pbkdf2_hmac を使わず、生のパスワードをハッシュ化している)
  • bcrypt の rounds < 12 — 総当たり攻撃に耐えるには ≥12 にする必要があります
🔴 重大 — 脆弱な乱数(CWE-330)
# BAD — random is NOT cryptographically secure
import random
token = random.randint(100000, 999999)       # predictable OTP
reset_key = random.choice(string.ascii_letters + string.digits)  # guessable

# GOOD
import secrets
token = secrets.randbelow(900000) + 100000   # secure OTP
reset_key = secrets.token_urlsafe(32)        # secure reset token
session_id = secrets.token_hex(16)           # secure session ID

確認項目: トークン、OTP、セッション ID、キー、またはノンスに関わるコードパスで、random.random()、random.randint()、random.choice()、random.shuffle() を使用している箇所。

🔴 高 — ハードコードされた暗号キー(CWE-321)
# BAD — key in source code
SECRET_KEY = "my-super-secret-key"
JWT_SECRET = "hardcoded_jwt_secret_123"
app.config['SECRET_KEY'] = "flask_secret"

# GOOD — load from environment
import os
SECRET_KEY = os.environ["SECRET_KEY"]        # raises KeyError if missing (intentional)
SECRET_KEY = os.getenv("SECRET_KEY")         # returns None silently (verify it's not None)

キーの最小長:

  • Flask SECRET_KEY:ランダムな 32 バイト以上(secrets.token_hex(32) を使用)
  • JWT HMAC シークレット:32 文字以上
  • AES キー:128、192、または 256 ビット(16、24、または 32 バイト)
🔴 高 — TLS/SSL 証明書の検証回避(CWE-295)
# BAD — disables certificate validation entirely
requests.get(url, verify=False)
httpx.get(url, verify=False)
ssl._create_unverified_context()
urllib3.disable_warnings()  # companion to verify=False

# GOOD
requests.get(url)               # verify=True is the default
requests.get(url, verify="/path/to/ca-bundle.crt")  # custom CA
🟡 中 — JWT の設定(CWE-327)
# BAD — algorithm confusion attacks
jwt.decode(token, key, algorithms=["none"])  # NEVER allow "none"
jwt.decode(token, key)                       # no algorithm pin = vulnerable

# BAD — weak secret loaded without validation
JWT_SECRET = os.getenv("JWT_SECRET", "default")  # fallback default is dangerous

# GOOD
JWT_SECRET = os.environ["JWT_SECRET"]        # fail hard if missing
assert len(JWT_SECRET) >= 32, "JWT secret too short"
jwt.decode(token, JWT_SECRET, algorithms=["HS256"])  # explicit allowlist

次の点も確認します。

  • トークンの有効期限(exp クレーム)— アクセストークンは 15〜30 分に設定します
  • デコード時に aud および iss クレームが検証されること
🟡 中 — 非推奨の共通鍵暗号(CWE-327)
# BAD — ECB mode leaks patterns in data
from Crypto.Cipher import AES
cipher = AES.new(key, AES.MODE_ECB)

# BAD — no authentication = vulnerable to tampering
cipher = AES.new(key, AES.MODE_CBC, iv)

# GOOD — authenticated encryption
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
# ChaCha20-Poly1305 is also acceptable

FastAPI 固有の確認項目

# 1. Verify OAuth2/JWT middleware algorithm pinning
from fastapi.security import OAuth2PasswordBearer
from jose import JWTError, jwt

ALGORITHM = "HS256"  # must be explicit, not a variable that could be overridden
# Check: is ALGORITHM loaded from config where it could be tampered with?

# 2. Verify password hashing in user creation/login endpoints
@router.post("/register")
async def register(user: UserCreate, db: Session = Depends(get_db)):
    hashed = pwd_context.hash(user.password)  # must NOT store plain or MD5

# 3. Token generation for password resets / email verification
def generate_reset_token() -> str:
    return secrets.token_urlsafe(32)   # GOOD
    # NOT: str(uuid.uuid4())  — UUID v4 uses os.urandom, acceptable but secrets is preferred
    # NOT: random-based anything

Flask 固有の確認項目

# 1. Session cookie signing uses SECRET_KEY — must be strong
app.secret_key = os.environ["SECRET_KEY"]  # GOOD
app.secret_key = "dev"                     # BAD — common in tutorials, dangerous in prod

# 2. Flask-Login password verification
from flask_bcrypt import Bcrypt
bcrypt = Bcrypt(app)
bcrypt.generate_password_hash(password, rounds=12)  # rounds≥12

# 3. HTTPS enforcement via HSTS
from flask_talisman import Talisman
Talisman(app, force_https=True)  # adds HSTS header

主な CWE リファレンス

CWE名称よくある例
CWE-327破損またはリスクのある暗号アルゴリズムの使用MD5、SHA1、ECB、DES、RC4
CWE-328可逆な一方向ハッシュパスワードへの MD5/SHA1 の使用(レインボーテーブルで元に戻せる)
CWE-330ランダム性が不十分な値トークン/キーに random モジュールを使用
CWE-321ハードコードされた暗号キーソースコード内のシークレット
CWE-759ソルトなしの一方向ハッシュhashlib.sha256(password) — ソルトなし
CWE-916計算コストが不十分なパスワードハッシュbcrypt rounds<12、PBKDF2 iterations<600k
CWE-295不適切な証明書検証HTTP クライアントでの verify=False

修正チェックリスト

最小限の暗号化スタック(Python 2025)
Password hashing:   passlib[argon2] or passlib[bcrypt] (rounds≥12)
Token generation:   secrets module exclusively
Symmetric crypto:   cryptography library, AES-GCM or ChaCha20-Poly1305
JWT:                python-jose or PyJWT, algorithm pinned, secret≥32 chars
TLS:                verify=True always; pin CA bundle where needed
Config/secrets:     os.environ (fail-hard) or a secrets manager (Vault, AWS SSM)
HSTS:               flask-talisman (Flask) or custom middleware (FastAPI)
コード検索に使える正規表現パターン
# Find weak hashing
grep -rn "hashlib\.md5\|hashlib\.sha1\|hashlib\.sha224" --include="*.py"

# Find insecure random
grep -rn "import random\b" --include="*.py"
grep -rn "random\.randint\|random\.choice\|random\.random()" --include="*.py"

# Find hardcoded secrets
grep -rn "SECRET_KEY\s*=\s*['\"]" --include="*.py"
grep -rn "verify=False" --include="*.py"

# Find ECB mode
grep -rn "MODE_ECB\|AES\.MODE_ECB" --include="*.py"

関連項目

  • references/owasp-a04-full.md — OWASP A04:2025 の完全な仕様と拡張 CWE リスト
  • サプライチェーンの暗号化に関する問題(依存関係のハッシュ固定)については、software-supply-chain スキルを参照してください
  • 暗号化以外の JWT 関連の認証ロジック(有効期限、クレーム)については、authentication-failures スキルを参照してください

コメント