Summary#
Crypto.Md5Hex(s) returns the MD5 digest of s's UTF-8 bytes as a 32-character lowercase hex string —
the canonical C# BitConverter.ToString(md5).Replace("-", "").ToLowerInvariant() form. It is deterministic:
the same input always yields the same digest.
Signature#
Crypto.Md5Hex(<string> s) -> stringDescription#
Not a security primitive#
MD5 is collision-broken. An attacker can construct two different inputs that produce the same digest — cheaply,
on a laptop. So Crypto.Md5Hex must never be used to sign, authenticate, verify, or fingerprint anything an
attacker could influence, and never for passwords. If a check answers the question "did this come from someone I
trust?" or "has anyone tampered with this?", MD5 is the wrong tool and using it there is a real vulnerability, not a
style issue.
Reach for these instead:
| If you need to… | Use |
|---|---|
| Hash a value that could be attacker-influenced | Crypto.Sha256Hex |
| Prove a message came from a key holder, unaltered | Crypto.HmacSha256Hex and Crypto.FixedTimeEquals |
| Store a password | Security.HashPassword (BCrypt — salted and deliberately slow) |
What it IS for — a non-adversarial content fingerprint#
Crypto.Md5Hex is kept because it is genuinely the right tool for a non-adversarial content fingerprint: hash a
synthesized string and compare it to a stored hash to decide whether downstream work needs to re-run. Nobody is
attacking your cache-invalidation key, and MD5 is cheap and stable.
Because it is deterministic, it pushes down into SQL: inside a query it renders as Postgres's md5(), which
produces byte-identical lowercase hex, so in-memory and in-database results agree. (Crypto.Sha256Hex pushes down too,
so you can use the secure hash in a query without giving that up.)
Examples#
// Safe use: deciding whether OUR OWN content changed, so we can skip redundant work.
// Nobody gains anything by forcing a cache miss here.
bool Changed(string content, string storedHash) {
return Crypto.Md5Hex(content) != storedHash;
}
// Crypto.Md5Hex("hello") -> "5d41402abc4b2a76b9719d911017c592"See also#
- Crypto.Sha256Hex — the secure default hash; use this whenever the input could be attacker-influenced
- Crypto.HmacSha256Hex and Crypto.FixedTimeEquals — authenticate a message under a shared key, and verify the tag in constant time
- Text.Split — other in-memory string builtins