6 password hashing myths, checked against OWASP and NIST
Store passwords with a slow, salted hash such as Argon2id, not SHA-256 or encryption. Six myths checked against OWASP, NIST SP 800-63B-4 and RFC 9106.
Advanced11 min read
same password + different salt = different hash user A pw "S3cret!"─┬ stored hash salt 9f2c41d7──┤ ┌─···················· │ │ │ ┌─────────────┐ │ └─────│ Argon2id │───┘ │ │ ┌─────│ ◐ ◓ ◑ │───┐ │ └─────────────┘ │ │ │ user B │ │ stored hash pw "S3cret!"─┼ └─···················· salt a71e0b3c──┘ db> (digests are illustrative)
On this page
- Myth 1: "Hashing and encryption are basically the same thing"
- Myth 2: "SHA-256 is a strong hash, so it's fine for passwords"
- Myth 3: "Salts are a secret ingredient, so we should hide them"
- Myth 4: "A pepper replaces good hashing"
- Myth 5: "Any 'slow' algorithm with any settings will do"
- Myth 6: "We can't upgrade old hashes without resetting everyone"
- Where network devices fit
- A short checklist
Password storage is one of those topics where everyone has heard the right answer ("hash it, don't encrypt it") and yet the wrong answers keep turning up: a column called password_encrypted, an unsalted SHA-256, a router config line that looks scrambled but isn't. The good news is that the guidance is unusually clear and free to read. The OWASP Password Storage Cheat Sheet, NIST SP 800-63B-4 and two RFCs cover almost everything.
So this post takes the common beliefs one at a time and checks each against those sources. Six myths, six facts, and a short section at the end on the network gear many of us look after every day.
Myth 1: "Hashing and encryption are basically the same thing"
Fact: they solve different problems, and for login passwords you want the one that cannot be undone.
Encryption is reversible by design: whoever holds the key can get the plaintext back. A hash is a one-way function. OWASP puts the distinction plainly: hashing is one-way, which makes it the right tool for checking a password, while encryption is two-way and only makes sense for passwords in the rare case where a system genuinely needs the original value again (OWASP, Hashing vs Encryption).
Think about what a login check actually needs. The server never has to know your password. It only has to confirm that what you typed now produces the same result as what you typed when you signed up. A one-way function gives exactly that, and nothing more. An encrypted password table, on the other hand, is one stolen key away from being a plaintext password table.
There is a real exception, and it is worth naming because network engineers live with it: some secrets have to be used, not just checked. A RADIUS or TACACS+ shared secret, or a routing protocol key, must be recovered in plaintext so the device can use it. Cisco's own paper on stored secrets calls these reversible credentials, in contrast to non-reversible ones such as login passwords, which the device only needs to verify (Cisco, Protecting Secrets on Cisco Network Devices). So the rule is not "never encrypt"; it is "never encrypt what you only need to verify."
Myth 2: "SHA-256 is a strong hash, so it's fine for passwords"
Fact: SHA-256 is a good hash for many jobs. Password storage is not one of them, because it is fast.
OWASP says it directly: fast hashing algorithms such as SHA-256 are not suitable for password storage, and password hashes should be slow, unlike algorithms such as MD5 and SHA-1, which were designed to be fast (OWASP, Introduction and Password Hashing Algorithms).
Why does speed matter if the hash can't be reversed? Because attackers don't reverse hashes; they guess. OWASP describes the loop: take a candidate from a wordlist, a list of passwords leaked from other sites, or a brute-force generator; hash it; compare the result with the stolen hash; repeat. Every part of that loop is limited by how quickly one hash can be computed. A hash designed to be fast, which is exactly what a general-purpose hash is meant to be, is a gift to that loop.
same time budget, very different cost per guess ┌─────────────────────────────┐ ┌────────────────────────────┐ │ fast hash (SHA-256) │ │ memory-hard (Argon2id) │ │ │ │ │ │ one guess: │ │ ························ │ │ █░░░░░░░░░░░░░░░░░░░░░░░░░ │ │ ························ │ │ │ │ ························ │ │ guesses tried: │ │ ························ │ │ │ │ ························ │ │ │ │ ························ │ │ │ │ │ │ │ │ guesses tried: 0 │ └─────────────────────────────┘ └────────────────────────────┘ time ●──────────────────────────────────────────────────────── crack> (illustrative, not to scale)
This idea is older than most of today's tooling. In 1999, Niels Provos and David Mazières introduced bcrypt in A Future-Adaptable Password Scheme, arguing that user-chosen passwords don't get stronger over time while hardware gets faster, so the cost of a password scheme has to be adjustable upward as hardware improves (USENIX ATC '99). That adjustable cost, the work factor, is the key feature. OWASP notes that the work factor is usually stored inside the hash output, so it can be raised later without breaking existing records (OWASP, Work Factors).
Memory-hard functions go one step further. Making each guess slow on a CPU is good; making each guess also need a large block of RAM makes it expensive to run thousands of guesses in parallel on specialised hardware. The scrypt RFC describes its function as built on memory-hard functions that offer extra protection against attacks using custom hardware (RFC 7914, Abstract), and RFC 9106 describes Argon2 as a memory-hard function aiming at the highest memory-filling rate (RFC 9106, Section 1).
Myth 3: "Salts are a secret ingredient, so we should hide them"
Fact: a salt is not a secret. Its job is to make every stored hash unique.
A salt is a random value generated per password and stored right next to the hash. It does two things, both described by OWASP: it stops attackers from using precomputed tables (rainbow tables and lookup databases), and it means you cannot tell whether two users share a password without cracking both hashes (OWASP, Salting).
same password + different salt = different hash user A pw "S3cret!"─┬ stored hash salt 9f2c41d7──┤ ┌─···················· │ │ │ ┌─────────────┐ │ └─────│ Argon2id │───┘ │ │ ┌─────│ ◐ ◓ ◑ │───┐ │ └─────────────┘ │ │ │ user B │ │ stored hash pw "S3cret!"─┼ └─···················· salt a71e0b3c──┘ db> (digests are illustrative)
NIST's verifier rules require passwords to be salted and hashed with a suitable password hashing scheme, with a salt of at least 32 bits chosen to minimise collisions, and the salt stored alongside the hash (SP 800-63B-4, Sec. 3.1.1.2). RFC 9106 suggests more for Argon2: 128 bits is enough for all applications, and 64 bits is acceptable where space is tight (RFC 9106, Section 4).
In practice you rarely handle salts yourself. OWASP points out that modern algorithms require a salt and that most widely used libraries generate and manage it internally (OWASP, Salting). If you find code that builds sha256(salt + password) by hand, that is a sign the real fix is a proper password hashing library, not a better salt.
Myth 4: "A pepper replaces good hashing"
Fact: a pepper is a useful extra layer, and that's all it is.
A pepper is a secret shared across all passwords and, unlike the salt, kept away from the database. OWASP says it should not be stored with the hashes, and suggests a secrets vault or an HSM. It can be applied before hashing, or after, by running the finished hash through an HMAC keyed with the pepper. The point is simple: an attacker who steals only the database (say, through SQL injection) cannot even start guessing without also stealing the pepper (OWASP, Peppering).
NIST describes the same idea without using the word: verifiers SHOULD add a keyed hashing or encryption step with a secret key known only to the verifier, generated by an approved random bit generator, stored separately from the hashes, ideally in hardware such as an HSM, TEE or TPM (SP 800-63B-4, Sec. 3.1.1.2).
Two caveats from OWASP keep peppers in perspective. A pepper is extra defence in depth, and on its own it provides no additional secure characteristics. And rotating one is painful: changing a pepper means forcing every user whose password was protected by the old pepper to reset it (OWASP, Peppering). Plan the key management before you ship it.
Myth 5: "Any 'slow' algorithm with any settings will do"
Fact: the algorithm matters, and so do its parameters. Here is where current guidance lands.
| Algorithm | When to use it (OWASP) | Minimum parameters (OWASP) |
|---|---|---|
| Argon2id | First choice | m = 19 MiB, t = 2, p = 1 (or equal-strength trade-offs such as 46 MiB / t = 1, or 12 MiB / t = 3) |
| scrypt | If Argon2id isn't available | N = 2^17 (128 MiB), r = 8, p = 1 |
| bcrypt | Legacy systems | work factor 10 or more; 72-byte input limit |
| PBKDF2 | When FIPS-140 validation is required | HMAC-SHA-256 with 600,000 iterations (HMAC-SHA-512: 220,000) |
All values above come from the OWASP cheat sheet, which lists a set of Argon2id configurations it says give an equal level of defence, differing only in how they trade CPU for RAM.
The specifications themselves set higher bars when you can afford them. RFC 9106, published in September 2021 by the IRTF's Crypto Forum Research Group, says Argon2id MUST be supported by any implementation, and gives two recommended settings: t = 1, p = 4, m = 2 GiB as the first choice, and t = 3, p = 4, m = 64 MiB when much less memory is available, both with a 128-bit salt and 256-bit tag (RFC 9106, Sections 1 and 4). Its tuning procedure is practical: work out how much memory each call can afford, then find the largest t that stays within your time budget, reducing memory if even t = 1 is too slow. For scrypt, RFC 7914 notes that r = 8 and p = 1 "appears to yield good results" (RFC 7914, Section 2).
Two practical edges deserve a mention:
- Don't tune yourself into an outage. OWASP's rule of thumb is that one hash should take less than a second, and warns that an excessive work factor can let attackers exhaust CPU with login attempts (OWASP, Work Factors).
- bcrypt's 72 bytes. OWASP recommends enforcing a 72-byte maximum and calls pre-hashing for bcrypt dangerous because of null-byte truncation and "password shucking"; if you must, it gives a specific HMAC-SHA384 plus base64 construction with the pepper outside the database (OWASP, bcrypt).
Where does PBKDF2 fit with NIST? SP 800-63B-4 says an approved scheme from the latest revision of SP 800-132 SHOULD be used (SP 800-63B-4). SP 800-132 dates from December 2010, and NIST noted in 2023 that it plans to revise it. That is why PBKDF2 is the usual pick in FIPS-bound environments.
While you are in SP 800-63B-4 (final since July 2025, per the NIST CSRC page), the verifier rules around hashing are worth a look too: at least 15 characters for passwords used alone (8 when part of MFA), allow at least 64, no composition rules, no forced periodic changes, a blocklist of common or compromised passwords, and rate limiting of failed attempts (SP 800-63B-4, Sec. 3.1.1.2). Hashing protects passwords at rest; these rules decide how guessable they are in the first place.
Myth 6: "We can't upgrade old hashes without resetting everyone"
Fact: you usually can, gradually.
OWASP describes the common approaches (OWASP, Upgrading Legacy Hashes):
- Rehash on login. When a user signs in successfully, you briefly have the plaintext. Verify it against the old hash, then hash it again with the new algorithm and save that.
- Wrap the old hash now. For accounts that won't log in soon, hash the existing hash with the strong algorithm (for example bcrypt over MD5). This needs no plaintext, but OWASP cautions that layered hashes can be easier to crack, so replace them with a direct hash at the next login.
- Expire the stragglers. For long-inactive accounts, remove the old hash and require a reset.
All of this is much easier if each record says how it was made. OWASP suggests storing the algorithm and work factor with the hash in a standard format such as the PHC string format (OWASP). The PHC string spec encodes the function, version, parameters, salt and hash in one line, for example $argon2id$v=19$m=65536,t=2,p=1$<salt>$<hash>. With that prefix, your login code can spot weak records and upgrade them as people sign in.
Where network devices fit
Everything above applies to the boxes in our racks too. Cisco's paper lists the credential "types" you see in IOS XE, IOS XR and NX-OS configs (Cisco, Protecting Secrets on Cisco Network Devices):
| Type | What it is | Cisco's view |
|---|---|---|
| 0 | Plain text | Not secure |
| 4 | Weak SHA-256 implementation | Not secure, removed |
| 5 | MD5 | Not secure |
| 6 | AES-128 with a device master key (reversible) | Secure |
| 7 | Vigenère cipher with a static, public key (reversible) | Not secure: treat as plain text |
| 8 | PBKDF2-SHA-256, 80-bit salt, 20,000 iterations | Secure |
| 9 | scrypt, 80-bit salt, 16,384 iterations | Secure |
| 10 | PBKDF2-HMAC-SHA512 (IOS XR only) | Secure |
The same paper describes a neat real-world example of Myth 6's "wrap the old hash": from IOS XE 16.12.x, type 5 secrets are converted automatically to type 9 by running scrypt over the existing MD5 value, and the device applies MD5 then scrypt when you log in. It also says type 9 is generally considered stronger than type 8, though some organisations choose type 8 for regulatory reasons, and it sets out the plan to remove type 0 and type 7 storage for reversible secrets in later IOS XE releases.
For me the practical takeaways when reviewing configs are short:
- A
password 7line is a readable password. Treat a config containing one as sensitive. - Prefer type 9 (or 8 where policy requires) for local accounts, and type 6 with a managed master key for secrets the device must reuse.
- Before a downgrade or an RMA, check which types the target release supports and where the master key is stored; Cisco's paper covers both scenarios.
A short checklist
- Login passwords: hashed with Argon2id (or scrypt, bcrypt, or PBKDF2 where required), never encrypted.
- Salts: unique per password, generated by the library.
- Pepper or keyed step: optional, kept outside the database, with a rotation plan.
- Parameters: at or above OWASP's minimums, benchmarked on your servers, stored with each hash.
- Legacy hashes: rehash on login, wrap the rest, expire the inactive.
- Devices: no type 0, 5 or 7 left in production configs.
None of this is exotic, and that is the encouraging part. The hard thinking has been done and written down; our job is mostly to read it and apply it.
Sources
- OWASP Password Storage Cheat Sheet
- NIST SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management
- NIST CSRC, SP 800-63B-4 publication page
- NIST SP 800-132, Recommendation for Password-Based Key Derivation, Part 1
- RFC 9106, Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications
- RFC 9106 on the IETF Datatracker
- RFC 7914, The scrypt Password-Based Key Derivation Function
- Provos and Mazières, A Future-Adaptable Password Scheme (USENIX ATC '99)
- C2SP, PHC string format
- Cisco, Protecting Secrets on Cisco Network Devices (PDF)
Keep reading
Related posts
Network security
AI model security by lifecycle stage: poisoning, pickles and theft
A five-stage checklist for protecting AI models, from data poisoning and code-running pickle files to model theft, based on NIST, NCSC/CISA, OWASP and ATLAS.
Advanced11 min read
- AI Security
- Machine Learning
- Supply Chain
- NIST AI RMF
- OWASP
- MITRE ATLAS
Network security
How SIEM and SOAR fit together: logs, alerts and playbooks
A SIEM collects, normalises and correlates logs into alerts; SOAR runs playbooks on them. Covers priority logs and Sigma rules, per NIST and ASD/CISA guidance.
Beginner10 min read
- SIEM
- SOAR
- logging
- detection
- Sigma
- CISA
- NIST
Network security
SD-WAN picks the path, SASE moves the inspection to the cloud
SD-WAN measures each link and moves flows to the path that meets the SLA; SASE moves SWG, FWaaS, CASB and ZTNA into cloud PoPs. Based on NIST SP 800-215.
Intermediate9 min read
- SD-WAN
- SASE
- ZTNA
- SWG
- CASB
- FWaaS
- Prisma Access
- Zscaler
- NIST
- network design