"Encode sensitive data" is one of the most dangerous phrases in software, because three completely different operations get collapsed into one word. Encoding, hashing, and encryption solve different problems, and picking the wrong one is how passwords end up "protected" by Base64 in a config file.
This guide draws the lines properly, then walks through the practical workflow for each case.
The one rule that matters
Encoding is not encryption. Base64, URL encoding, hex - these are reversible by design, by anyone, with no key. They exist to make data transportable, not secret. If your protection scheme can be undone by pasting the string into a free web tool, it isn't protection.
Everything else in this guide is detail. If you only remember one thing, remember that.
Encoding vs hashing vs encryption
| Reversible? | Needs a key? | Solves | |
|---|---|---|---|
| Encoding (Base64, URL, hex) | Yes, by anyone | No | "This data won't survive this transport" |
| Hashing (SHA-256, bcrypt) | No, one-way | No (salt isn't a key) | "I need a fingerprint, or to verify without storing the original" |
| Encryption (AES-GCM, age, TLS) | Yes, with the key | Yes | "Only authorised parties may read this" |
Three questions route you to the right one:
- Does the data need to be readable later by you or a system? If no - you only ever need to verify it, like a password - hash it.
- Does it need to be hidden from anyone in between? If yes, encrypt it.
- Does it just need to survive a text-only channel, a URL, or a JSON field? That's encoding, and encoding alone implies zero secrecy.
These compose. A common real pipeline is: encrypt the secret, then Base64-encode the ciphertext so it fits in an environment variable. The Base64 is packaging; the AES is the protection.
Step 1. Classify the data first
Before touching any tool, decide what you're actually holding:
- Credentials and API keys - need encryption at rest (a secret manager) and TLS in transit. Never hash them if a system must use the value; never merely encode them.
- Passwords you verify but never read back - hash, with a password-specific algorithm (bcrypt, scrypt, Argon2). Not plain SHA-256, which is too fast to resist brute force.
- PII in transit between your own services - TLS covers transport; encoding covers formatting.
- Non-secret structured data that breaks transports - binary blobs in JSON, tokens in URLs - plain encoding is fine, because secrecy was never the requirement.
Most "how do I encode this sensitive data" questions dissolve at this step, because the real answer is "you don't encode it, you encrypt or hash it."
Step 2. Use encoding for transport problems
Legitimate encoding jobs:
- Embedding binary data (certificates, images, signed payloads) in JSON, YAML, or env vars - Base64.
- Putting arbitrary strings into URLs or query parameters - URL encoding, or URL-safe Base64 for binary.
- Basic auth headers -
Authorization: Basic <base64(user:pass)>, which is exactly why basic auth is only acceptable over TLS: the Base64 hides nothing.
Open the Base64 Encoder / Decoder, encode your input, and immediately decode it back to confirm a clean round trip. If the data is going into a URL or filename, use the URL-safe variant (- and _ instead of + and /) or it will corrupt in transit.
One caution: don't paste actual production secrets into any web tool, ours included, as a matter of hygiene. Use the tool to design and debug the scheme with test values; encode the real secret locally:
printf '%s' 'test-value' | base64
Step 3. Use hashing for verification problems
A hash is a fixed-length, one-way fingerprint. Same input, same output, every time; no path back to the input. Use it when you need to verify data without possessing it, or detect tampering:
- File integrity - publish the SHA-256 of a release artifact; consumers hash their download and compare.
- Change detection - hash a config or record, store the digest, re-hash later to detect drift.
- Password storage - store the hash (bcrypt/Argon2 with salt), verify by hashing the login attempt.
The Hash Generator gives you MD5, SHA-1, SHA-256 and friends from the same input. Practical guidance:
- SHA-256 is the default for integrity work.
- MD5 and SHA-1 are cryptographically broken for security purposes (collisions are practical). They survive only as legacy checksums - fine for detecting accidental corruption, unacceptable for anything an attacker might target.
- For passwords specifically, fast hashes like SHA-256 are also wrong - you want deliberately slow, salted algorithms. A GPU can attempt billions of SHA-256 guesses per second.
And the giveaway difference from encoding: you can never "decode" a hash. Websites claiming to reverse hashes are doing lookup tables of known inputs, not reversal.
Step 4. Use encryption for secrecy problems
If unauthorised parties must not read the data, you need encryption with real key management:
- In transit - TLS. Solved problem; just don't ship plaintext HTTP for anything sensitive.
- At rest, application secrets - a secret manager (AWS Secrets Manager, Vault, Doppler, your platform's encrypted env vars) rather than hand-rolled crypto.
- At rest, files and backups -
ageor GPG for files, full-disk or volume encryption underneath. - In your own code - AES-256-GCM via your language's vetted library (libsodium,
crypto.subtle,cryptography). Don't invent modes, don't reuse nonces, don't hardcode keys next to the ciphertext.
The hard part of encryption is never the algorithm; it's where the key lives. A key committed to the same repo as the ciphertext is Base64 with extra steps.
Step 5. Verify the round trip and document the scheme
Whatever combination you land on, prove it end to end before shipping: encode/encrypt on one side, decode/decrypt on the other, byte-compare against the original. For hashes, verify the same input produces the same digest across both systems (watch for trailing-newline and UTF-8 differences - echo vs printf has burned everyone at least once).
Then write the scheme down where the next engineer will find it: what's encoded vs encrypted, which algorithm, where keys live, and which fields in the database or payload are protected by what. Undocumented schemes get "simplified" into plaintext during refactors.
Common mistakes
Base64-ing passwords and calling it done. This is obfuscation, not protection. It stops grep, not attackers.
Hashing data you need back. Hashes are one-way. If a system must use the original value (an API key it sends to a vendor), hashing it makes it unusable - encrypt instead.
Encrypting passwords instead of hashing them. Encryption is reversible by whoever holds the key, which means a key compromise leaks every password. Verification-only data should be hashed so there's nothing to leak.
Using MD5 or SHA-1 for anything security-relevant. Legacy checksums only. New work uses SHA-256 or better.
Confusing salt with a key. A salt is public, per-record randomness that defeats precomputed tables. It doesn't make a hash secret and it isn't an encryption key.
Double-encoding. Base64-encoding an already-encoded string, or URL-encoding twice, produces garbage that "decodes" into more garbage. Decode in the reverse order you encoded.
TL;DR
- Classify first: transport problem → encode; verification problem → hash; secrecy problem → encrypt.
- Base64 and URL encoding are reversible by anyone - they are never the protection layer.
- Use the Base64 Encoder / Decoder for transport encoding and round-trip checks.
- Use the Hash Generator with SHA-256 for integrity; bcrypt/Argon2 for passwords; never MD5/SHA-1 for security.
- Real secrets get real encryption plus key management - a secret manager, not a clever string transform.
- Verify the full round trip, then document the scheme.
Related
- How to decode Base64 - the mechanics of the encoding layer
- Base64 Encoder / Decoder - both directions, URL-safe included
- Hash Generator - fingerprints and integrity checks
