Security

Hashing, encryption and encoding: the differences

Base64, SHA-256 and AES do things that, at first glance, look identical: a readable text goes in, a pile of scrambled characters comes out. That is where serious security bugs are born, people "encrypting" a password with Base64, storing passwords as MD5, or treating a hash as if it could be reversed. The difference fits in two questions: can you get back to the original? And, if so, do you need a key? Encoding reverses with no secret at all (it is not security); encryption reverses only with the right key; a hash does not reverse at all, by design. This guide separates the three, shows why hashing a password is not like hashing a file, and why SHA-256, which is excellent for files, is terrible for passwords precisely because it is too fast.

J-Kit16 min readIntermediate
  • Hashing
  • Encryption
  • Encoding
  • Security

Key takeaways

  • Encoding (Base64, hex) is reversible and secret-free: it is for transport and representation, never for protection.
  • Hashing is irreversible by design: there is no un-hashing. It is for integrity and, with salt and a slow function, for passwords.
  • Encryption is reversible with a key: it is for confidentiality. A password is never encrypted.
  • SHA-256 is too fast for passwords (billions of guesses per second on a GPU), hence bcrypt, scrypt and Argon2.

Three operations, one misunderstanding

The three techniques scramble text, but they solve different problems. Encoding wants to fit a channel and be read back; hashing wants a fingerprint that proves nothing changed; encryption wants to hide content from anyone without the key. Instead of memorizing examples, keep the two axes that separate everything: reversibility and the presence of a key. Encoding is reversible with no key; encryption is reversible with a key; hashing is not reversible at all.

Encoding

  • Reversible? Yes, by anyone.
  • Needs a key? No.
  • Good for: transporting and representing bytes (Base64, hex, URL).

Encryption

  • Reversible? Yes, with the right key.
  • Needs a key? Yes, that is the whole point.
  • Good for: confidentiality (AES, RSA).

Hashing

  • Reversible? No, by design.
  • Needs a key? No (only HMAC uses one).
  • Good for: integrity, fingerprinting, salted passwords.

The fastest test is to run the same text through all three operations and see what can be undone in each case. Below, the word senha123 turns into three very different outputs, and only one of them needs a secret to get back to the original.

Entrada / Input:  senha123

Base64   ->  c2VuaGExMjM=
             reverte sem segredo: qualquer um lê "senha123" de volta
             reverses with no secret: anyone reads "senha123" back

SHA-256  ->  55a5e9e78207b4df8699d60886fa070079463547b095d1a05bc719bb4e6cd251
             irreversível: só dá para adivinhar a entrada e recalcular
             irreversible: you can only guess the input and recompute

AES-GCM  ->  (bundle base64, muda a cada execução)
             reverte apenas com a senha correta
             reverses only with the correct passphrase
The same input through three paths. Base64 comes back on its own, the hash never comes back, and AES only comes back with the key. Try it yourself in the [hash generator](tool:gerador-hash) and the [text encoder](tool:codificador-texto).

Encoding: represent, do not protect

Encoding translates data from one format to another by a public, reversible rule. The goal is to fit a channel. Base64, defined in RFC 4648, packs three bytes (24 bits) into four 6-bit characters drawn from a 64-symbol alphabet, so you can send binary over email or in JSON without breaking anything. Hexadecimal shows each byte as two readable digits. URL-encoding escapes special characters inside a link. There is no key and no secret in any of them: whoever holds the encoded value always recovers the original.

"oi"  ->  Base64     ->  "b2k="      (qualquer um reverte / anyone reverses)
"oi"  ->  hex        ->  "6f69"
"a b" ->  URL-encode ->  "a%20b"

// Base64url (RFC 4648 §5): variante URL-safe, troca + por - e / por _,
// e descarta o padding "=". É a codificação de tokens, JWT e chaves em URL.
Encoding is public and reversible. base64url is the variant that fits in URLs and headers, the same one used in the parts of a [JWT](guide:jwt-anatomia-assinatura-seguranca).

Hashing: the irreversible fingerprint

A hash function turns any input into a fixed-size output. It is deterministic (the same input always gives the same output), sensitive (changing one bit changes the whole result, the avalanche effect) and, when cryptographic, infeasible to reverse. The SHA-1 and SHA-2 families (SHA-256, SHA-384, SHA-512) live in NIST’s FIPS 180-4; the SHA-3 family, based on Keccak, lives in FIPS 202, approved on 5 August 2015. There is no decrypting a hash: verification means recomputing it and comparing. A checksum like CRC32 is not a cryptographic hash, it catches accidental error, not tampering; it is the same family as the check digit of the Brazilian CPF and CNPJ.

The strength of a cryptographic hash is measured by three resistances. They do not fall together: a function may still resist preimage attacks while having already lost collision resistance, which is exactly what happened to SHA-1.

Preimage resistance
Given a hash h, it is infeasible to find any input m with hash(m) = h. This is what stops you from reversing the digest of a file or a password.
Second-preimage resistance
Given an input m1, it is infeasible to find a different m2 with the same hash. Protects a specific document from being swapped for another with the same digest.
Collision resistance
It is infeasible to find any pair m1 not equal to m2 with the same hash. It is the easiest to break (the birthday bound) and was the first to fall for MD5 and SHA-1.

Why is collision the easiest? Because of the birthday paradox. Finding an input that matches a fixed hash costs about 2^n tries (n is the hash size in bits). But finding any two inputs that collide with each other costs only about 2^(n/2), because each new input can collide with all the previous ones. It is the same reason why, in a room of 23 people, the chance that two share a birthday already tops 50%.

colisao_esperada ~= 2^(n/2) SHA-256 -> 2^128 MD5 -> 2^64
n
hash output size, in bits (SHA-256 = 256, MD5 = 128)
2^(n/2)
number of inputs tried before you expect two with the same hash
Effort to find a collision in an n-bit hash, in the ideal case (no flaw in the algorithm).
2^128ideal collision in SHA-256 (~3.4 × 10^38 tries)
2^64ideal collision in MD5 (~1.8 × 10^19 tries)
2^24REAL MD5 collision today: seconds on a laptop

First worked example, in numbers: doubling the hash from 128 to 256 bits raises the ideal collision effort from 2^64 to 2^128, a factor of 2^64, almost 18.5 quintillion times more work. That is why 256 bits is not "twice" 128: it is exponentially more. But look at the third column: for MD5, the ideal 2^64 is fiction. The 2004 cryptanalysis dropped the real cost to around 2^24, MD5 collisions come out in seconds on a laptop. A "128-bit" hash with a broken algorithm is not worth 2^64; it is worth almost nothing. Output size only protects while the algorithm inside stays sound. Generate and compare digests in the hash generator below, and recognize a digest by its shape with the hash identifier.

The generator covers MD5, SHA-1, the SHA-2 family, checksums (CRC32, Adler-32, FNV-1a) and HMAC, all in the browser. Type a text and watch the avalanche effect by changing a single character.Open the tool full page

When a hash breaks: MD5 and SHA-1

Breaking a hash almost never means reversing the output, it means finding a collision that should not exist with so little effort. For MD5, collisions for the full function were announced by Wang, Feng, Lai and Yu in August 2004. For SHA-1, the blow came in two waves. In February 2017, the SHAttered attack by CWI and Google produced two different PDFs with the same SHA-1 hash, spending about 2^63.1 operations, well below the 2^80 the theory predicted. In January 2020, Leurent and Peyrin published SHA-1 is a Shambles, a chosen-prefix collision: the attacker fixes two arbitrary, different beginnings and still makes the hashes match. That is the kind of collision that forges a certificate or a signature, and it cost around US$45,000 in rented GPUs, cheap for a determined attacker.

One honest detail: not every SHA-1 resistance fell. Collision resistance was broken; preimage resistance (finding an input for a given hash) still has no practical attack. Even so, a hash that lost collision resistance is unfit for signatures or certificates, which is why browsers and certificate authorities retired SHA-1. To choose what to use today, the table below sums up the state of each family.

Quick reference. The [hash generator](tool:gerador-hash) covers MD5, SHA-1, SHA-2, checksums and HMAC; SHA-3 and the password functions (bcrypt, scrypt, Argon2) are separate categories, not included in it.
AlgorithmOutputStatus
MD5128 bitsDo not use, practical collisions since 2004.
SHA-1160 bitsDo not use, practical (2017) and chosen-prefix (2020) collisions.
SHA-256 / SHA-512256 / 512 bitsUse, current standard for integrity (FIPS 180-4).
SHA-3 (Keccak)224–512 bitsUse, sponge construction, immune to length extension (FIPS 202).
CRC32 / Adler-3232 bitsAccidental error only, not cryptographic, no tamper resistance.
HMAC-SHA-256256 bitsUse, integrity and authenticity with a secret key.
bcrypt / scrypt / Argon2variableUse, passwords only: deliberately slow and tunable.
  1. 1992MD5 (RFC 1321)

    Ronald Rivest publishes MD5, the 128-bit digest that dominates the decade.

  2. 1995SHA-1 (FIPS 180-1)

    NIST publishes SHA-1, at 160 bits, as a sturdier successor.

  3. 2004MD5 collisions

    Wang et al. announce collisions for the full MD5 at CRYPTO 2004.

  4. 2015SHA-3 standardized (FIPS 202)

    NIST approves the Keccak/SHA-3 family on 5 August; sponge construction.

  5. 2015Argon2 wins the PHC

    The Password Hashing Competition picks Argon2 as the reference password function (July).

  6. 2017SHAttered

    CWI and Google produce the first practical SHA-1 collision (23 February).

  7. 2020SHA-1 is a Shambles

    Leurent and Peyrin demonstrate a chosen-prefix collision for ~US$45,000.

  8. 2021RFC 9106

    The IETF standardizes Argon2 for password hashing and proof-of-work (September).

Encryption: reversible with a key

Encryption scrambles content so that only someone with the key recovers it. In symmetric encryption, the same key encrypts and decrypts, it is fast and ideal for bulk data; AES is the example. In asymmetric encryption, a public/private key pair lets anyone encrypt to you but only you decrypt (RSA, elliptic curves), which solves key exchange and digital signatures, the basis of HTTPS. One essential caveat: encrypting is not merely hiding, it is hiding in an authenticated way. AES-GCM is an AEAD (authenticated encryption) and, beyond secrecy, produces a tag that detects any tampering. And never use ECB mode, which leaks the structure of the text, the penguin case, at the end of this section, shows why.

AEAD (AES-GCM)
Authenticated encryption: besides ciphering, it produces an integrity tag. The text encryption tool uses 256-bit AES-GCM with the key derived from your passphrase via PBKDF2-HMAC-SHA-256.
Asymmetric (RSA / ECDH)
Public/private pair. ECDH agrees on a key without sending it in the clear; RSA and ECDSA sign. No secret needs to be shared beforehand.
HMAC (RFC 2104)
A keyed hash that proves integrity AND authorship of a message. Immune to length extension by construction, which is why it replaces hash(key ‖ message).

The site’s text encryption tool is a good place to see the difference in practice: you type a passphrase, it derives the key via PBKDF2 and encrypts with AES-GCM; without the passphrase, the ciphertext is noise. If a system can show you the original value back without you typing any key, then it was not encryption, it was encoding in disguise. A JWT illustrates the opposite trap: it is signed, not encrypted, and the payload is readable by anyone; the JWT guide shows why confusing signing with encrypting puts sensitive data in the wrong place.

Passwords: slow salted hashing, never encryption

The classic mistake is "encrypting the password". Encryption is reversible: if the server can recover the text, a leak can too. The correct approach is to store the password’s hash, so the real value is never kept. But here is the point that separates password hashing from file hashing: for a file, you want the hash to be fast, because you only need to verify integrity in milliseconds. For a password, "fast" is exactly the flaw. A GPU computes billions of SHA-256 per second; if the database leaks, the attacker tries billions of guesses per second against each hash. SHA-256 is great for files and terrible for passwords for the same reason: speed.

The solution is functions built for passwords: deliberately slow, with an adjustable cost factor and a random salt per password. bcrypt (Provos and Mazières, 1999) was the pioneer; scrypt (RFC 7914) and Argon2 (RFC 9106, winner of the 2015 Password Hashing Competition) are memory-hard, meaning they also demand a lot of memory, which spoils the GPU advantage. PBKDF2 (RFC 8018) is the oldest and relies only on iterations. The OWASP Password Storage Cheat Sheet currently recommends, as a starting point, the parameters in the table:

Minimum parameters recommended by OWASP (2025). Raise the cost until the hash takes roughly 0.5 to 1 second on your server.
FunctionCost parameter (minimum)When to choose
Argon2id19 MiB memory, 2 iterations, parallelism 1First choice for new code.
scryptN = 2^17, r = 8, p = 1Memory-hard alternative when Argon2 is unavailable.
bcryptwork factor 10+ (72-byte limit)Legacy systems already on bcrypt.
PBKDF2-HMAC-SHA-256600,000 iterations or moreWhen FIPS-140 compliance is required.
Salt
A random value, unique per password, stored alongside the hash. It makes two identical passwords produce different hashes and defeats precomputed rainbow tables.
Pepper
A server-wide secret, mixed into the password before hashing and stored OUTSIDE the database. If only the database leaks, the hash alone is not enough to attack.

Second worked example, with the real value: if two users pick the password senha123, the SHA-256 of both is exactly 55a5e9e7…4e6cd251, identical. The attacker computes that digest once, looks it up in the list of leaked hashes and cracks both at once; and since senha123 is already in every rainbow table, that is instant. Now add a random per-user salt: the hashes become different, the precomputed table is useless, and Argon2 forces the attacker to spend memory and time per guess. The salt does not make the password stronger, it destroys the attack’s economy of scale.

  • Use Argon2id, scrypt or bcrypt, never plain SHA-256, to store passwords.
  • A random salt per password, stored alongside the hash.
  • Never store a password as plain text, Base64 or plain MD5/SHA.
  • Raise the cost factor until the hash takes 0.5 to 1 second on your server.

The bcrypt generator shows what a password hash looks like with the salt and cost factor baked into the string itself. And before storing any password, the guide how to choose and test a strong password helps the user avoid handing over the very input the attacker wants most.

Length extension: why hash(key ‖ message) fails and HMAC fixes it

MD5, SHA-1 and the SHA-2 family use the Merkle–Damgård construction: they process the message in blocks, and the final hash is, essentially, the machine’s internal state. The problem appears when someone authenticates a message with hash(key ‖ message). An attacker who knows that hash and the message length can resume the computation from the final state and produce a valid hash for hash(key ‖ message ‖ padding ‖ extension), without knowing the key. That is the length extension attack, and it forges authenticated messages.

The standard fix is HMAC (RFC 2104), which nests two hashes with the key and does not expose the internal state as output. That is why HMAC exists: it is not "another hash", it is the correct way to key a hash. The newer constructions do not even need it for this: SHA-3, being a sponge, and BLAKE2 do not suffer length extension by design.

Why SHA-256 is terrible for passwords and great for files

It is the same property, speed, seen from two angles. To verify a 4 GB download, you want the hash as fast as possible: SHA-256 processes the file in a blink and confirms integrity. To store a password, you want the hash as slow as possible: every extra millisecond multiplies the cost of a brute-force attack. Because SHA-256 is optimized for speed, it hands the attacker billions of tries per second on cheap hardware. Password functions invert that on purpose, with a cost factor and (in scrypt and Argon2) a memory requirement.

ECB mode and the penguin: why you never encrypt in ECB

AES encrypts 16-byte blocks. In ECB mode (Electronic Codebook), each plaintext block is encrypted in isolation, so identical blocks become identical ciphertext blocks. Encrypt an image with flat color areas (the classic is Linux’s Tux penguin) in AES-ECB and the penguin’s outline is still visible in the ciphertext: the encryption hid the bytes but preserved the pattern. It is the most famous demonstration that ECB must not be used.

The way out is to use a mode with a random IV and, preferably, authenticated: AES-GCM, as this site’s encryption tool does. Each run produces a different ciphertext for the same input, and the authentication tag detects tampering.

Frequently asked questions

Can you decrypt a SHA-256 hash?
No. A hash is irreversible by design; there is no key to reverse it. Anyone who "cracks" a hash is really guessing the input and recomputing until it matches, which is why passwords need slow, salted functions that make that guess-and-recompute expensive.
Is Base64 encryption?
No. Base64 is encoding: public and reversible by anyone, with no key. It is for representing bytes as text (transport, JSON, URL), not for protecting information. Storing a password in Base64 is the same as storing it in plain text.
Why can’t I store passwords with SHA-256?
Because SHA-256 is too fast: a GPU computes billions per second, so if the database leaks, the attacker tries billions of guesses per second. Also, without a salt, identical passwords produce identical hashes and fall to rainbow tables. Use Argon2id, scrypt or bcrypt, with a per-password salt.
Is SHA-1 still secure?
Not for security. SHA-1’s collision resistance fell with SHAttered (2017) and SHA-1 is a Shambles (2020), which made a chosen-prefix collision for about US$45,000. That is enough to forge certificates and signatures. Use SHA-256 or SHA-3.
What is the difference between a hash and an HMAC?
A plain hash proves integrity: it shows the bytes did not change. HMAC (RFC 2104) uses a secret key, so it proves integrity AND authorship, only someone with the key produces a valid HMAC. It also avoids the length extension attack, which affects the naive hash(key ‖ message) construction.
Which hash should I use to verify a file?
SHA-256 for a security guarantee. CRC32 only detects accidental transmission error and does not resist deliberate tampering. Avoid MD5 and SHA-1 for any security guarantee, because an attacker can forge collisions.

Ask about reversibility and the key: coming back with no secret is encoding (transport, not protection); coming back only with the key is encryption (confidentiality); never coming back is hashing (integrity). A password is never encrypted, it is slow-hashed with salt in Argon2, scrypt or bcrypt, because SHA-256 is too fast. And remember that collision is the most fragile resistance: it is the one that fell for MD5 and SHA-1.

Sources & references

  1. NIST FIPS 180-4, Secure Hash Standard (SHA-1, SHA-2)
  2. NIST FIPS 202, SHA-3 Standard (Keccak)
  3. The first collision for full SHA-1 (SHAttered), Stevens et al., 2017
  4. RFC 9106, Argon2 Memory-Hard Function for Password Hashing
  5. RFC 2104, HMAC: Keyed-Hashing for Message Authentication
  6. RFC 4648, The Base16, Base32, and Base64 Data Encodings
  7. OWASP, Password Storage Cheat Sheet