Server Implementation
Password storage uses MD5 or SHA-1 — fast, GPU-accelerated, unsalted by default — or bcrypt with fewer than 10 rounds. None of these are password hashes; cracking the resulting digest with a modern GPU is a matter of hours, not years.
Cryptographic hash functions and password-hashing functions solve different problems. MD5 and SHA-1 are designed to be fast — exactly the wrong property when an attacker who exfiltrates the hash table will run billions of guesses per second on rented hardware. bcrypt, scrypt, and argon2id deliberately slow themselves down with a tunable cost parameter ("rounds") so the attacker's brute-force loop is throttled too. bcrypt with rounds<10 falls back into the fast-hash regime and stops being a password hash in any meaningful sense.
MCP servers occasionally store user credentials directly — for self-hosted deployments, admin login pages, or per-tool API keys assigned to teammates. Authors reach for `hashlib.md5(password)` because it is the first hashing primitive the standard library exposes, and the surrounding code looks plausible. The bug rarely shows up in security review because the surface is small and the test case ("login works") doesn't exercise the threat model. When a database is leaked, the difference between MD5 and argon2id is the difference between every password being public and most passwords surviving.
import hashlib |
def store_password(password: str) -> str: |
# MD5 is a fast hash, not a password hash — GPU-crackable. |
return hashlib.md5(password.encode()).hexdigest() |
from argon2 import PasswordHasher |
ph = PasswordHasher() # tuned defaults: argon2id, t=3, m=64MB, p=4 |
def store_password(password: str) -> str: |
return ph.hash(password) |
def verify_password(stored: str, candidate: str) -> bool: |
try: |
return ph.verify(stored, candidate) |
except Exception: |
return False |
MCPSafe flags `hashlib.md5(...)` / `hashlib.sha1(...)` whose argument name matches `password` / `passwd` / `pwd` / `secret`, and `crypto.createHash("md5"|"sha1")` in JavaScript. Also flags `bcrypt.gensalt(rounds=N)` / `bcrypt.gensalt(N)` where `N < 10`. Hashing constants or non-password identifiers (e.g. ETag, content-addressable IDs) is not flagged.
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now