Basic Web App Security: When and What to Encrypt
March 22, 2025
So you've built your first web application—congratulations! Now comes an equally important part: keeping your users' data safe. Let's talk about encryption and when to use it.
Why Encryption Matters
Think of a postcard — everyone who handles it can read it. That's what HTTP communication looks like: every network node between the browser and the server sees the data. Encryption turns that postcard into a sealed envelope that only the recipient can open.
Encryption isn't a "premium feature" to add before launch. It's a foundation — without it, every other security measure you put in place has limited value.
HTTPS: Your First Line of Defense
Threat: Without HTTPS, every request — login credentials, form data, session tokens — travels in plaintext. A man-in-the-middle attack: an attacker on the same network (coffee shop, airport) can passively eavesdrop, steal passwords and tokens, or even inject malicious code into server responses. No specialized equipment required — just a laptop and a tool like Wireshark.
HTTPS (Hypertext Transfer Protocol Secure) encrypts the connection between the user's browser and the server:
- Data is encrypted in transit — even if someone intercepts the traffic, they see a garbled stream
- The TLS certificate verifies the server's identity — the user knows they're connecting to the real domain, not an impostor
- Data integrity — content cannot be modified without detection
Why this works: The TLS handshake negotiates an encrypted channel between client and server before any data is transmitted. A certificate issued by a trusted Certificate Authority (CA) cryptographically binds the domain to a public key — an attacker can't forge a certificate for your domain without compromising the CA or the domain itself.
Implementing HTTPS with Let's Encrypt
Let's Encrypt makes setting up HTTPS incredibly easy and free! Here's what you need to know:
- Let's Encrypt is a Certificate Authority (CA) that provides free SSL/TLS certificates
- You can use tools like Certbot to automatically set up certificates
- Certificates need renewal every 90 days, but most tools can handle this automatically
To get started, visit the Let's Encrypt website and follow their instructions for your specific server setup. Modern hosting platforms often include one-click HTTPS setup as well.
What Data Should Be Encrypted?
Not all data needs the same level of protection. Here's what you should focus on:
Always Encrypt:
- Passwords (with proper hashing, which we'll discuss later)
- Financial information (credit card numbers, bank details)
- Personal identifiable information (full names, addresses, birth dates)
- Authentication tokens
- Health information
- Private messages between users
The golden rule is: if data would cause harm if leaked, encrypt it.
Encryption at Rest vs. In Transit
Your data exists in different states, and each needs protection:
Data in Transit: Information moving between systems (user to server, server to database)
- Protected by: HTTPS, SSL/TLS connections to databases
Data at Rest: Information stored in your database or files
- Protected by: Database encryption, encrypted file systems, field-level encryption
Always ensure you're protecting data in both states.
Hashing vs. Encryption: What's the Difference?
Threat: Databases leak. It's not a question of "if" but "when." If passwords are stored in plaintext or in a weak format (MD5, SHA-1), a database breach means all users' passwords are exposed. An attacker can also run a dictionary attack or a rainbow table attack — they have tables of pre-computed hashes for common passwords and just compare them against your database.
This is a critical distinction that many new developers mix up:
Encryption is a two-way process:
- You can encrypt data and later decrypt it with the right key
- Think of it as locking a box — you can always unlock it if you have the key
- Use for data that needs to be retrieved in its original form later
Hashing is a one-way process:
- Once data is hashed, you cannot recover the original input
- You can only verify if a given input matches the hash
- Perfect for passwords — you never need to know the actual password, just verify if what the user entered matches
For passwords, always use a strong hashing algorithm with "salting" (adding random data to each password before hashing). Today, bcrypt, Argon2, or PBKDF2 are the right algorithms for the job.
Why this works: Hashing is one-directional — there's no decryption key an attacker could steal. A salt (a unique random value per password) means two identical passwords produce different hashes — rainbow tables become useless. A high work factor (bcrypt/Argon2 computational cost) means every password guess costs time: testing a million passwords could take years instead of seconds.
Best Practices for Storing Sensitive User Information
- Never store plaintext passwords. Use strong hashing algorithms with salts.
- Minimize stored data. The best way to protect data is not to store it at all. Ask yourself: "Do I really need this information?"
- Separate sensitive data from regular data when possible. Consider using different databases with stricter access controls.
- Use environment variables for storing encryption keys and credentials, never hardcode them or commit them to version control.
- Implement proper access controls to limit who can see what data, even within your own team.
These fundamentals cover the most common failure modes. Getting them right first means everything else builds on solid ground.
HSTS — So the Browser Doesn't Even Try HTTP
You have HTTPS. But what if a user types vibedefend.com into the browser instead of https://vibedefend.com? The browser sends the request over HTTP, the server redirects to HTTPS — and there's a moment where traffic travels unencrypted.
On a public network (coffee shop, airport) that moment is enough for a man-in-the-middle attacker to intercept the request before the redirect happens. This attack is known as SSL stripping.
HSTS (HTTP Strict Transport Security) solves this with a header the browser remembers:
Strict-Transport-Security: max-age=31536000; includeSubDomainsAfter the first HTTPS visit, the browser will automatically convert every http://vibedefend.com to https://vibedefend.com for a year — before sending any data. The SSL stripping attack stops working because there's no longer any HTTP request to intercept.
How to set in nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;In Express.js (via Helmet):
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true
}));Before enabling includeSubDomains — make sure all your subdomains have a TLS certificate. If api.yourdomain.com doesn't support HTTPS, enabling HSTS will make it inaccessible to browsers that have already visited the main domain.
Mixed Content — When HTTPS Isn't Enough
You have HTTPS across your entire site. But somewhere in the code:
<img src="http://cdn.example.com/logo.png">
<script src="http://analytics.example.com/tracker.js">This is mixed content — an HTTPS page loading resources over HTTP. Browsers warn about or block such resources, because one weak link destroys the protection of the entire connection. A script loaded over HTTP can be swapped out by a man-in-the-middle.
Active mixed content (scripts, stylesheets, iframes) — browsers block by default. Passive mixed content (images, audio, video) — browsers show a warning but usually load anyway.
How to check and fix:
- Open DevTools → Console — mixed content warnings appear there
- Find all resources loading over HTTP and change them to HTTPS or
//(protocol-relative URL) - For your own resources: enforce HTTPS on your CDN or storage server
The CSP header can help detect:
Content-Security-Policy-Report-Only: upgrade-insecure-requestsOr automatically upgrade HTTP → HTTPS:
Content-Security-Policy: upgrade-insecure-requestsTLS Versions — Old Means Dangerous
TLS has versions: 1.0, 1.1, 1.2, 1.3. Versions 1.0 and 1.1 have known vulnerabilities (BEAST, POODLE, CRIME) and were officially deprecated by the IETF in 2021. Supporting them on your server is an open invitation to attacks.
Minimum standard today: TLS 1.2. Preferred: TLS 1.3 — faster (one roundtrip instead of two during the handshake) and more secure (eliminates weak cipher algorithms).
Check your server configuration:
# Quick test via SSL Labs
# (online: https://www.ssllabs.com/ssltest/)
# Locally via nmap
nmap --script ssl-enum-ciphers -p 443 yourdomain.comThe result should show only TLS 1.2 and 1.3, with no weak cipher suites (RC4, 3DES, NULL).
Enforce minimum TLS 1.2 in nginx:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;Certificate Expiry Monitoring
Threat: An expired certificate means HTTPS stops working. The browser displays a security warning and blocks access to the application. Users see a red screen saying "your connection is not private" — for most people, that's a signal to close the tab. A certificate expiring in production is an availability outage.
A Let's Encrypt certificate is valid for 90 days. Certbot and most platforms (Heroku, Render, Vercel, Cloudflare) renew it automatically. But automation can fail — certbot hangs, DNS stops matching, something goes wrong.
Set up monitoring:
- UptimeRobot — free SSL monitoring, alert 30 days before expiry
- Certbot with cron —
certbot renewin cron checks every 12h and renews if < 30 days remaining - Let's Encrypt sends email 20, 10, and 1 day before expiry (to the address on the certificate) — make sure that email is monitored
Why this works: External monitoring (UptimeRobot, Datadog, Uptime Kuma) verifies the certificate from the browser's perspective — it catches cases where automatic renewal fails silently. A 30-day alert before expiry gives enough buffer for manual intervention, even if the alert is delayed or missed.
Summary
Encryption is several independent layers, each protecting against a different attack vector — they work together, not instead of each other:
| Technique | Protects against |
|---|---|
| HTTPS | MITM, eavesdropping, content injection |
| Password hashing (bcrypt/Argon2) | Database breaches, rainbow tables |
| HSTS | SSL stripping on public networks |
| No mixed content | HTTP resources undermining HTTPS protection |
| TLS 1.2+ | Attacks on deprecated protocols (BEAST, POODLE) |
| Certificate monitoring | Availability outage from expired certificate |
If you're looking for broader context on which data to encrypt and in which states (in transit vs. at rest) — Securing Your Database goes deeper on encryption at the database and file system level. Managing permissions for encrypted data is covered in Implementing Basic Permission Systems.
Sources: OWASP — Cryptographic Failures, HSTS — MDN, Mixed Content — MDN, Deprecating TLS 1.0 and 1.1 — RFC 8996