Skip to main content
Cloud & AI Hub
Browse
Glossary AI Directory Playgrounds Models Prompts Explainers Strategy Matrix Benchmark Decoder
beginner 7 mins Read

Understanding Hashing vs. Encryption vs. Encoding

The critical differences between one-way hashing, reversible encryption, and data encoding — and the security implications of confusing them.

Introduction

Hashing, encryption, and encoding are often confused — sometimes with serious security consequences. Storing passwords as base64 “encoded” strings (which are trivially reversible) or using MD5 to “encrypt” sensitive data (it’s actually a hash — and a broken one) are mistakes that have caused massive data breaches. These three operations serve entirely different purposes, require different algorithms, and should never be substituted for one another.

flowchart LR
    I["Input data"] --> H["Hashing (SHA-256, bcrypt)"]
    I --> E["Encryption (AES, Fernet)"]
    I --> C["Encoding (Base64, URL)"]
    H --> H2["Fixed-length digest, one-way — cannot recover input"]
    E --> E2["Ciphertext, reversible only with the key"]
    C --> C2["Reformatted output, reversible by anyone, no key"]

Hashing: One-Way Fingerprinting

A hash function maps arbitrary input to a fixed-length digest. It is a one-way operation — you cannot recover the original input from the hash:

import hashlib, bcrypt

# Cryptographic hash (NOT for passwords)
data = b"sensitive-data"
sha256_digest = hashlib.sha256(data).hexdigest()
print(sha256_digest)  # 64-char hex — no way to reverse to "sensitive-data"

# Password hashing (bcrypt — correct approach)
password = b"user-password-123"
hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))
print(hashed)  # $2b$12$... — includes salt, work factor

# Verification
is_valid = bcrypt.checkpw(b"user-password-123", hashed)  # True
is_valid = bcrypt.checkpw(b"wrong-password", hashed)     # False

Use hashing for:

  • Password storage (bcrypt, Argon2, scrypt — never MD5/SHA1/SHA256)
  • Data integrity verification (SHA-256 checksums)
  • HMAC signatures for webhook payloads

Never use:

  • MD5 or SHA1 for passwords (rainbow tables, fast brute-force)
  • SHA-256 alone for passwords (no salt, too fast — use bcrypt/Argon2)

Encryption: Two-Way Transformation

Encryption transforms plaintext into ciphertext using a key, and can be reversed (decrypted) with the appropriate key:

from cryptography.fernet import Fernet
import os

# Symmetric encryption (AES-128-CBC under the hood)
key = Fernet.generate_key()  # Store this securely!
cipher = Fernet(key)

plaintext = b"Secret customer data: SSN 123-45-6789"
ciphertext = cipher.encrypt(plaintext)
print(ciphertext)  # b'gAAAAAB...' — unintelligible without key

decrypted = cipher.decrypt(ciphertext)
print(decrypted)   # b"Secret customer data: SSN 123-45-6789"

# Asymmetric encryption (RSA) — encrypt with public key, decrypt with private
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()

ciphertext = public_key.encrypt(plaintext, padding.OAEP(
    mgf=padding.MGF1(algorithm=hashes.SHA256()),
    algorithm=hashes.SHA256(), label=None
))
original = private_key.decrypt(ciphertext, padding.OAEP(
    mgf=padding.MGF1(algorithm=hashes.SHA256()),
    algorithm=hashes.SHA256(), label=None
))

Use encryption for:

  • Data at rest (database columns with sensitive PII)
  • Data in transit (TLS)
  • Secrets storage (AWS KMS, HashiCorp Vault)

Encoding: Reversible Representation Change

Encoding transforms data between representations without secrecy — it is trivially reversible and provides zero security:

import base64, urllib.parse

# Base64 encoding — anyone can decode this
data = "Secret: password123"
encoded = base64.b64encode(data.encode()).decode()
print(encoded)        # "U2VjcmV0OiBwYXNzd29yZDEyMw=="
decoded = base64.b64decode(encoded).decode()
print(decoded)        # "Secret: password123"

# URL encoding
url_encoded = urllib.parse.quote("hello world & special=chars")
print(url_encoded)    # "hello%20world%20%26%20special%3Dchars"

Use encoding for:

  • Transmitting binary data in text channels (base64 for email attachments)
  • URL-safe characters (percent encoding)
  • Data format conversion

Never confuse encoding with security — base64 is not encryption.

Decision Matrix

ScenarioCorrect ApproachWrong Approach
Store user passwordsbcrypt/Argon2MD5, SHA256, base64
Encrypt DB credit card columnAES-256 encryptionHashing (can’t decrypt)
Verify file integritySHA-256 hashEncryption (overkill)
Send binary in JSONbase64 encodingEncryption
API webhook authenticationHMAC-SHA256 signaturebase64 “signature”

Key Takeaways

  • Hashing is one-way and irreversible — use it for password storage (bcrypt/Argon2) and integrity verification (SHA-256 checksums).
  • Encryption is two-way and reversible with the correct key — use it for data that must be retrieved in plaintext (PII, secrets).
  • Encoding (base64, URL encoding) is reversible by anyone — it provides zero security and should never be used as a security measure.
  • Never store passwords as SHA-256 hashes — they have no salt and are too fast to compute, enabling rainbow table and brute-force attacks.
  • bcrypt work factor (rounds parameter) intentionally makes hashing slow — higher rounds = slower brute-force, choose based on acceptable latency.

Historical figures, architectures, and capabilities are for informational purposes only. Not technical, professional, legal, or financial advice. Sources: Research papers, developer documentation.