Skip to content
AldeaCode Logo
Hash Generator / Python Developer 100% local

Hash strings in Python: hashlib, encoding, and the password trap

Python ships `hashlib` in the standard library and that is genuinely all you need for checksums, content-addressed storage, or ETags. The mistake everyone makes the first time is forgetting that `hashlib` wants bytes, not strings.

hashlib is the standard library answer

hashlib is built in since forever and exposes SHA-1, SHA-256, SHA-384, SHA-512, SHA-3 family, BLAKE2 and the legacy MD5. The API is the same across every algorithm:

```py import hashlib

digest = hashlib.sha256(b"AldeaCode").hexdigest() ```

The constructor takes bytes. If you pass a str you get TypeError: Strings must be encoded before hashing. The fix is one call to .encode("utf-8"). Always be explicit about the encoding. Defaults vary across platforms and you do not want a Mac-vs-Windows hash mismatch when an accented character sneaks into the input.

Encoding is part of the contract

Two strings that look identical can still produce different hashes if their underlying bytes differ. The classic case is composed vs decomposed Unicode: "café" can be U+00E9 or U+0065 U+0301. They render the same. They hash differently.

If you are hashing user input that could come from different platforms, normalise first:

```py import unicodedata, hashlib

def stable_sha256(text: str) -> str: normalized = unicodedata.normalize("NFC", text) return hashlib.sha256(normalized.encode("utf-8")).hexdigest() ```

This sounds pedantic until you see two records in your database that should match by content hash and do not.

hmac is for keyed hashes

If you need to verify that a message was produced by someone who knows a shared secret (webhook signatures, API request signing, JWT-HS256 verification), do not concatenate secret + payload and hash that. Use hmac from the standard library.

```py import hmac, hashlib

signature = hmac.new( key=secret.encode("utf-8"), msg=payload.encode("utf-8"), digestmod=hashlib.sha256, ).hexdigest() ```

Use hmac.compare_digest (constant time) to verify, never ==, to avoid timing attacks. The Python docs explicitly call this out.

bcrypt or argon2 for passwords

hashlib.sha256 is not a password hash. It is too fast. A consumer GPU does billions of SHA-256 attempts per second, so a 10-char password is brute-forceable in hours.

For passwords install argon2-cffi or bcrypt:

```py from argon2 import PasswordHasher

ph = PasswordHasher() hashed = ph.hash("correct horse battery staple") ph.verify(hashed, "correct horse battery staple") ```

Argon2id is the OWASP recommended default since 2021. bcrypt is fine if you have an existing schema. Plain SHA never.

Working example

python
import hashlib
import hmac
import unicodedata

# Stable hash of arbitrary text input
text = "AldeaCode"
normalized = unicodedata.normalize("NFC", text)
sha256 = hashlib.sha256(normalized.encode("utf-8")).hexdigest()
print(sha256)

# Webhook signature verification
secret = b"shared-secret"
payload = b'{"event":"deploy"}'
signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print(signature)

Just need the result?

When you just need to hash a single string outside any pipeline, like checking a release checksum or eyeballing what `sha256("hello")` returns, opening a Python shell is too many steps. Paste the value into the browser-based hash generator and read all four SHA variants at once, with zero install.

Open SHA Hash Generator →

Frequently asked questions

Why does hashlib reject my string?

hashlib only accepts bytes. Call .encode('utf-8') on the string first. Always specify the encoding explicitly so the hash is reproducible across platforms.

Is hashlib.sha256 thread-safe?

The constructor is. The returned hash object is not. If two threads hash different inputs, give each its own hashlib.sha256() instance instead of sharing one and calling update.

Should I use hashlib.sha3_256 over hashlib.sha256?

For new applications either is fine. SHA-3 has a different internal construction (Keccak) and is not vulnerable to length-extension attacks, which matters for raw HMAC-style schemes only. SHA-256 is faster on most hardware.