Back to Blog
Security13 min readJun 2026

HTTPS, TLS & Encryption Basics

Base64 is not encryption, and you must never encrypt a password. A from-scratch tour of TLS, certificates, symmetric vs asymmetric keys, and the difference between hashing, encryption, and encoding every engineer needs.

SecurityTLSEncryptionHTTPS
SB

Sri Balaji

Founder · TheSimplifiedTech

On this page

Base64 is not encryption

A junior engineer once shipped a feature that stored API tokens "encrypted" in a database column. Code review looked clean. Then someone pasted one value into an online decoder, hit a button, and the raw token appeared in plain text. The "encryption" was Base64, a format that anyone can reverse in one click, with no key, no secret, nothing. The data had never been protected at all.

This mistake is everywhere, and it comes from blurring three words that sound similar but do completely different jobs: encoding, encryption, and hashing. Get them confused and you ship things that *look* secure and protect nothing. This article untangles them, then walks through how HTTPS actually protects data on the wire with TLS.

Who this is for

Engineers who use HTTPS every day but have never seen what happens during the handshake, and anyone who has ever wondered whether they should encrypt a password (you should not). No cryptography background needed; if you can read a function, you can follow this.

A postcard versus a sealed envelope

Encryption turns readable data into unreadable data using a key, such that only someone with the right key can turn it back. Without encryption in transit, every byte you send is a postcard.

Plain HTTP is a postcard. Every router, Wi-Fi access point, and ISP between you and the server can read, and rewrite, everything: the page you requested, the form you submitted, the session cookie that proves who you are. Nothing stops a coffee-shop attacker from copying it as it passes.

HTTPS is a sealed, tamper-evident envelope. TLS (Transport Layer Security, the protocol behind the "S") does three things at once: it encrypts the contents so onlookers see scrambled bytes, it authenticates the server so you know you are really talking to your bank and not an impostor, and it provides integrity so any tampering en route is detected and the connection is dropped.

Sealed envelope nobody can read throughEncryption, contents are unreadable in transit
Return address you trust and can verifyAuthentication, the certificate proves server identity
Tamper-evident seal that shows if openedIntegrity, any modification breaks the connection
A postcard anyone in the mail chain can readPlain HTTP, every hop sees and can alter the data
Three properties of a sealed envelope map directly to the three guarantees of TLS.

What happens when you connect (the TLS handshake)

Before any real data flows, the client and server run a short negotiation called the handshake. Its job is to verify the server's identity and agree on a shared secret key, all over an untrusted network where an attacker may be listening. Here is the flow at a high level.

1. hello2. negotiate3. send certtrust chain4. verify5. exchangeshared key6. encrypt all
Browser

Client

ClientHello

Cipher suites offered

Certificate

Verify vs CA

Key Exchange

Derive session key

Server

example.com

Certificate Authority

Trusted root

Encrypted Channel

Symmetric AES

A TLS connection: the client verifies the certificate, both sides exchange keys, then all traffic flows through an encrypted channel.

  1. 1

    ClientHello

    The browser opens with the TLS versions and cipher suites it supports, plus a random value. This is the menu of cryptography it is willing to use.

  2. 2

    ServerHello + Certificate

    The server picks a cipher suite and sends back its certificate, a signed document containing its public key and domain name.

  3. 3

    Verify the certificate

    The browser checks the certificate is for the right domain, not expired, and signed by a Certificate Authority it already trusts. If any check fails, you get the scary red warning page.

  4. 4

    Key exchange

    Using the server's public key (asymmetric crypto), both sides securely agree on the same secret session key. Modern TLS uses ephemeral exchange so each session gets a fresh key.

  5. 5

    Switch to the session key

    From here on, both sides encrypt with that one shared symmetric key, fast, and impossible to read without it.

  6. 6

    Encrypted application data

    Your actual HTTP request and the server's response now travel through the sealed channel. The handshake is done in a few milliseconds.

Certificates and Certificate Authorities

Encryption alone is not enough, you also need to know *who* you are encrypting to. A certificate is the server's identity document: it binds a domain name to a public key and is digitally signed by a Certificate Authority (CA), an organization your operating system and browser already trust out of the box.

When the browser verifies a certificate, it follows the chain of trust: the site's cert is signed by an intermediate CA, which is signed by a root CA whose public key ships inside your device. If the chain leads back to a trusted root and nothing is expired or mismatched, the identity is accepted. A self-signed certificate has no such chain, which is why browsers reject it for public sites.

Free, automated certs

There is no excuse for plain HTTP anymore. Let's Encrypt issues trusted certificates for free and tools like Certbot renew them automatically. HTTPS everywhere is the baseline, not a premium feature.

Symmetric vs asymmetric keys

TLS uses two kinds of cryptography, and the handshake is clever precisely because it combines them. Symmetric encryption uses one shared key for both locking and unlocking, it is fast, but both sides must somehow already share the secret. Asymmetric encryption uses a key pair: a public key anyone can use to encrypt, and a matching private key only the owner holds to decrypt.

Asymmetric crypto solves the chicken-and-egg problem of "how do we share a secret over a network we do not trust?", but it is slow. So TLS uses asymmetric crypto *once* during the handshake to safely agree on a symmetric session key, then uses the fast symmetric key for all the actual data. Best of both worlds: secure setup, fast transfer.

Hashing vs encryption vs encoding

This is the table to memorize. Three operations, three completely different purposes. The single most common security bug in junior code is reaching for the wrong one.

PurposeReversible?Use case
EncodingRepresent data in a format for safe transport or storageYes, no key needed, anyone can reverse itBase64 in a data URL, URL-encoding, UTF-8. Not security at all.
EncryptionKeep data confidential from anyone without the keyYes, but only with the correct keyTLS in transit, encrypting a database or secrets at rest.
HashingProduce a fixed, irreversible fingerprint of dataNo, one-way by designStoring passwords, verifying file integrity, digital signatures.
Same input, very different guarantees. Reversibility is the dividing line.

Read the middle column slowly. Encoding is reversible by anyone, it is a format, not a lock, so it provides *zero* confidentiality. Encryption is reversible only with the key, so it protects data you need to read back later. Hashing is *not reversible at all*, which is exactly why it is the right tool for passwords.

Why you hash passwords and never encrypt them

You never need to recover a user's original password, you only need to check that the password they typed at login matches the one they set. That is a comparison, not a retrieval, which is precisely what a one-way hash gives you. If you *encrypted* passwords instead, the key that decrypts them exists somewhere, and the day your database leaks alongside that key, every password is exposed in plain text.

But a plain hash is not enough either. Two users with the same password would get the same hash, and attackers precompute huge "rainbow tables" of common passwords. The fix is a salt, a unique random value mixed into each password before hashing, plus a deliberately *slow* algorithm so brute-forcing is expensive. Use a purpose-built password hash like bcrypt, scrypt, or Argon2; never a fast general hash like MD5 or SHA-1.

passwords.py
python
import bcrypt


def hash_password(plaintext: str) -> bytes:
    # bcrypt generates a random salt and embeds it in the output.
    # The work factor (rounds) makes each hash deliberately slow.
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(plaintext.encode("utf-8"), salt)


def verify_password(plaintext: str, stored_hash: bytes) -> bool:
    # We never decrypt anything. We hash the input the same way
    # and compare. bcrypt reads the salt back out of stored_hash.
    return bcrypt.checkpw(plaintext.encode("utf-8"), stored_hash)


# Sign-up: store ONLY the hash, never the plaintext.
stored = hash_password("correct horse battery staple")

# Login: compare, do not retrieve.
print(verify_password("correct horse battery staple", stored))  # True
print(verify_password("wrong password", stored))                # False

There is no decrypt() here on purpose

Notice the code never recovers the original password. If you ever find yourself wanting a function that returns a user's password in plain text, stop, you are about to build the breach.

In transit vs at rest

Encryption protects data in two different states, and you need both. In transit means data moving across a network, protected by TLS, the encrypted channel from the handshake above. At rest means data sitting in storage: a database, a disk, an S3 bucket, a backup, protected by encrypting the storage itself, so a stolen drive or leaked snapshot is unreadable.

They cover different threats. TLS does nothing for a backup file someone copies off your server; disk encryption does nothing for a token sniffed on open Wi-Fi. A secure system encrypts in transit *and* at rest. For protecting whole APIs end to end, see our companion piece on Secure API Design.

Common mistakes that cost hours (or breaches)

  1. Treating encoding as encryption. Base64 and hex are formats, not locks. If there is no key, there is no protection, anyone can decode it instantly.
  2. Encrypting passwords instead of hashing them. Reversible storage means a leaked key exposes every password. Hash with bcrypt, scrypt, or Argon2 and a per-user salt.
  3. Using fast hashes for passwords. MD5 and SHA-1 are built for speed, which is exactly what an attacker wants. Use a slow, salted password hash.
  4. Allowing weak or old TLS. SSL, TLS 1.0, and TLS 1.1 are deprecated and exploitable. Require TLS 1.2 or 1.3 and modern cipher suites only.
  5. Self-signed certificates in production. They break the chain of trust and train users to click through warnings. Use a real CA, Let's Encrypt is free.
  6. Disabling certificate verification to "make it work." Turning off validation in an HTTP client silently removes the authentication guarantee and invites man-in-the-middle attacks.

Takeaways

The whole article in seven lines

  • HTTPS = HTTP inside TLS, which gives you encryption, server authentication, and integrity.
  • The TLS handshake verifies a certificate, then agrees on a shared key over an untrusted network.
  • Certificates bind a domain to a public key and are trusted via a CA chain back to a root your device already trusts.
  • Asymmetric keys set up the connection securely; the fast symmetric session key carries the actual data.
  • Encoding is reversible by anyone (a format), encryption is reversible with a key, hashing is one-way.
  • Hash passwords with a salt and a slow algorithm (bcrypt/scrypt/Argon2), never encrypt them.
  • Encrypt data both in transit (TLS) and at rest (storage), they defend against different threats.

Where to go next

Encryption basics are the foundation of nearly every security topic above them. From here, build outward into how you design protected systems and networks.

Want to go deeper?

This article covers concepts taught hands-on in the Cloud Engineer and DevOps career paths, with real terminal labs, production scenarios, and structured lessons.