One article on our blog has consistently remained our most popular: "How SHA-256 works: Can you decrypt it?" We decided to bring a few ideas from it to Dev.to, and we hope the topic prompts some of you to challenge, refine, or expand on these ideas in the comments. Let's get into it.
SHA-256 only runs one direction
"Can SHA-256 be decrypted?" sounds like a reasonable question, but it mixes two different operations.
Encryption is designed to be reversible: data is transformed using a key, and someone with the appropriate key can recover the original plaintext. SHA-256 works differently. It takes an input and produces a fixed-length digest, and the algorithm only computes that digest forward.
Password hashes remain attackable through a different route: guessing.
Suppose an attacker obtains a database containing SHA-256(password) values. They can take a likely password, hash it, and compare the result with the stored value. A match reveals the password that produced that verifier.
Hashing and encryption solve different problems. Reversal resistance blocks one type of attack. Guessing remains available as another.
What SHA-256 does
SHA-256 accepts a message of arbitrary length and produces a deterministic 256-bit digest. Identical input produces identical output, and even a small change to the input radically changes the resulting digest.
Internally, SHA-256 pads the message, processes it in 512-bit blocks, expands each block into a message schedule, and runs a compression function for 64 rounds. For password-storage decisions, one design characteristic matters most: SHA-256 is built to compute digests efficiently.
That efficiency is useful. SHA-256 appears in integrity checks, digital-signature workflows, and other systems where processing data quickly is exactly what developers want. Passwords create a different problem.
Why fast hashing helps attackers too
During a normal login, computing one SHA-256 digest quickly looks ideal. After a database leak, that same speed benefits the attacker.
An offline attacker works independently of your application. Rate limits, API throttling, and account lockouts stay outside the loop, because the attacker tests candidate passwords against a stolen verifier using their own hardware.
For a general-purpose hash function, making each calculation inexpensive is useful engineering. For password storage, we want each guess to carry a meaningful computational cost.
That is why the OWASP Password Storage Cheat Sheet recommends dedicated password-hashing schemes over fast general-purpose hashes such as SHA-256.
The important distinction sits between a cryptographic primitive and the system built around it. SHA-256 can remain a secure hash function while SHA-256(password) remains a poor password-storage design.
Salting and key derivation solve different problems
Adding a salt is an essential improvement, but its purpose is sometimes misunderstood.
A salt is a unique random value associated with each password record. Two users choosing the same password therefore produce different stored values. Salting also defeats reusable precomputation: an attacker cannot prepare one table of common password hashes and apply it efficiently across many users or databases.
A salt makes every stored password record a distinct target. Making each guess expensive is a separate job, handled by a password key derivation function (KDF).
The salt normally sits alongside the password verifier, so once an attacker steals the database, they know it. They can still take a candidate password, combine it with that user's salt, calculate the result, and compare it with the stored verifier.
A purpose-built password KDF handles that second job by deliberately increasing the cost of each attempt. Argon2id and PBKDF2, for example, let systems tune the amount of work required to derive a verifier.
The controls solve different problems:
| Control | Main job | Where it falls short |
|---|---|---|
| Unique random salt | Prevents shared precomputation and makes identical passwords hash differently | Guessing speed against one known record stays the same |
| Argon2id / PBKDF2 | Makes each offline guess more expensive | A weak password stays weak |
| Rate limiting + MFA | Reduces online account-takeover risk | A stolen hash database still needs its own protection |
These mechanisms complement each other. Each covers a different part of the threat.
What to use in a new system
For a new application, a sensible default is usually Argon2id. OWASP currently recommends it as the preferred password-hashing choice. When FIPS-140 requirements apply, OWASP recommends PBKDF2-HMAC-SHA-256 with a work factor of at least 600,000.
Treat PBKDF2-HMAC-SHA-256 as a distinct construction from plain SHA-256. SHA-256 may be part of the construction, but PBKDF2 combines a pseudorandom function with a salt and configurable work factor specifically to make repeated guesses more expensive.
NIST SP 800-63B similarly describes password hashing around salts and cost factors, with the cost increased over time as system performance permits.
Treat those parameters as configuration: benchmark them on your infrastructure, document the decision, and revisit it as hardware and recommendations evolve.
Applications also need an upgrade path. A maintained framework or cryptographic library makes that easier than implementing the scheme yourself. Store enough metadata with each verifier, including algorithm version, salt, parameters, and verifier, to recognize older records and migrate them later.
Beyond password hashing: the controls you still need
A good password KDF mainly changes the economics of offline guessing after a verifier database has been stolen. It addresses one threat model: offline guessing after a leak. Other attack paths need separate controls.
Production systems still need controls around the login flow.
Rate limiting raises the cost of online guessing. MFA reduces the value of a stolen password. Password policies and breached-password checks reduce predictable credentials. Phishing-resistant authentication addresses attacks that password hashing never sees.
Operational planning matters too. Parameters that looked reasonable several years ago may eventually become cheap to compute. One practical migration strategy detects an outdated verifier after a successful login and derives a new one using the current scheme and parameters.
For password storage, choose a purpose-built password-hashing scheme such as Argon2id or PBKDF2-HMAC-SHA-256. Use maintained libraries, design for parameter upgrades, and treat authentication as a system: password hashing is one component among several.
SHA-256(password) creates a security problem while SHA-256 itself remains cryptographically sound. The real question is how expensive we make each attacker guess after the database is already outside our control.
What password-hashing scheme does your stack use today, and how do you plan and execute parameter or algorithm upgrades as a gradual rollout instead of a risky big-bang migration?
References
How SHA-256 works: Can you decrypt it? — Passwork.pro
OWASP Password Storage Cheat Sheet
NIST SP 800-63B: Digital Identity Guidelines

Top comments (0)