2026-09-04 · 8 min read
- passwords
- unicode
- security
Users enter characters, transport layers carry encoded bytes, and password-hashing implementations consume a byte sequence. Those lengths differ for non-ASCII text, combining marks, and emoji sequences.
If a system silently truncates at a byte boundary, two visibly different passwords can share the same effective prefix. If registration and authentication normalize differently, the correct user input may never reproduce the stored hash.
Define one input pipeline
Document how clients and servers preserve the entered value, which character encoding becomes hash input, whether normalization occurs, and what maximum encoded length the selected implementation supports.
- Never trim, case-fold, or silently truncate passwords.
- Apply the same policy at registration, login, and password change.
- Reject over-limit input before expensive hashing.
- Keep password values out of logs, analytics, and error reports.
Use a password-hashing implementation
Passwords require a salted, deliberately expensive password-hashing algorithm such as Argon2id, scrypt, bcrypt, or PBKDF2 according to current platform guidance. A fast SHA digest is not a substitute.
Configure cost parameters for the deployment's latency and memory budget, generate a unique random salt per record, and store algorithm and parameter metadata so future upgrades are possible.
Migrate legacy records safely
When historical records used different encoding, normalization, truncation, or hash parameters, retain enough metadata to verify them through the exact legacy path. After successful authentication, hash the original entered password with the current policy and replace the record.
- Do not try several transformations indefinitely.
- Rate-limit authentication and migration work.
- Use constant-time library verification APIs.
- Invalidate exposed credentials rather than merely rehashing them.
Test without exposing credentials
Use Flashman's password generator for synthetic values, units converter for byte budgets, Base64 to preserve harmless byte fixtures, diff for normalization demonstrations, and hash only to label public fixtures—not to store passwords.
Test ASCII and non-ASCII input, composed and decomposed forms, emoji sequences, exact byte boundaries, malformed encodings, client copy and paste, password-manager fills, legacy migration, denial-of-service limits, and every supported runtime.