v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: MEDIUM auth.py.crypto.passlib-weak-scheme

passlib configured with a weak or plaintext password scheme.

Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.

Why this matters

A CryptContext lists a password hashing scheme that is broken for storing credentials: unsalted or fast digests (hex_md5, hex_sha1, hex_sha256, md5_crypt, ldap_md5), legacy des_crypt, or outright plaintext / ldap_plaintext. These are trivially brute-forced or reversed, so any leaked hash exposes the underlying password (CWE-916).

Use a slow, salted, memory-hard scheme as the default, e.g. CryptContext(schemes=["argon2"]) or CryptContext(schemes=["bcrypt"]). Keep a weak scheme only as a deprecated verifier during migration, never as an active hashing scheme.

VULNERABLE
vulnerable.py
from passlib.context import CryptContext

# ruleid: auth.py.crypto.passlib-weak-scheme
pwd_context = CryptContext(schemes=["md5_crypt"], deprecated="auto")

# ruleid: auth.py.crypto.passlib-weak-scheme
plain_context = CryptContext(schemes=["plaintext"])

# Weak digest scheme mixed into the list.
# ruleid: auth.py.crypto.passlib-weak-scheme
mixed_context = CryptContext(schemes=["hex_sha1", "des_crypt"])

# ruleid: auth.py.crypto.passlib-weak-scheme
ldap_context = CryptContext(schemes=["ldap_plaintext"], deprecated="auto")
SAFE
safe.py
from passlib.context import CryptContext

# ok: auth.py.crypto.passlib-weak-scheme
bcrypt_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

# ok: auth.py.crypto.passlib-weak-scheme
argon2_context = CryptContext(schemes=["argon2"])

# Modern schemes only; "sha256_crypt" must not match the "hex_sha256" entry.
# ok: auth.py.crypto.passlib-weak-scheme
strong_context = CryptContext(schemes=["argon2", "pbkdf2_sha256", "sha256_crypt"])

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

# oauthlint-disable-next-line auth.py.crypto.passlib-weak-scheme -- <reason>

References

https://passlib.readthedocs.io/en/stable/lib/passlib.context.html ↗https://cwe.mitre.org/data/definitions/916.html ↗