Encryption Standards
From the CTO — Required reading for all engineering and infrastructure teams. Encryption is not optional. It is a baseline requirement for every system we build.
Table of Contents
- Why This Matters
- The Standard at a Glance
- Data at Rest: AES-256
- Data in Transit: TLS 1.2+
- Forbidden Algorithms
- Quick Reference
Why This Matters
Encryption protects our users and our business. When we get it wrong, the consequences are permanent.
What is at risk
User data gets exposed. If a database is leaked and data is not encrypted, user passwords, personal information, and payment data are immediately readable by anyone who downloads the file. There is no recovery from that.
We fail compliance audits. SOC 2 and GDPR both require strong encryption for data at rest and in transit. A failed audit or a data breach can block enterprise sales, trigger large fines, and in some regions, require notifying every affected user publicly.
We lose trust — permanently. Startups depend on trust. A single security incident that exposes user data because of weak encryption can end a company. The cost of doing it right now is nothing compared to the cost of a breach.
The rule is simple: If data is sensitive, it must be encrypted. No exceptions.
The Standard at a Glance
| Data State | Standard | Where it Applies |
|---|---|---|
| At Rest | AES-256-GCM | Databases, S3 buckets, disk volumes, backups, secrets |
| In Transit | TLS 1.2 or TLS 1.3 | APIs, web traffic, internal microservice calls, webhooks |
Data at Rest: AES-256
What "data at rest" means
Data at rest is any data that is stored — not moving across a network. This includes:
- Database rows (user records, payment info, PII)
- Files stored in S3 or cloud storage
- Disk volumes on servers and containers
- Backups and exports
- Secrets and credentials stored in config files or vaults
All of it must be encrypted using AES-256.
Why AES-256
AES-256 is the current global standard for symmetric encryption. It is used by governments, banks, and every major cloud provider. There is no credible attack against it when used correctly. It is fast, well-supported in every language, and battle-tested in production at scale.
We use AES-256-GCM specifically. GCM (Galois/Counter Mode) provides both encryption and message authentication in one step. This means you cannot tamper with the ciphertext without detection.
Infrastructure requirements
- All RDS/database instances: enable encryption at rest (AWS KMS or equivalent).
- All S3 buckets: server-side encryption with AES-256 (SSE-S3 or SSE-KMS). No bucket may be unencrypted.
- All EBS volumes and container disk mounts: encrypted by default.
- All secrets (API keys, credentials): stored in a secrets manager (AWS Secrets Manager, HashiCorp Vault), never in environment files committed to git.
Architectural warning: Enabling disk-level encryption on your cloud provider is necessary but not sufficient. Sensitive fields (passwords, SSNs, tokens) must also be encrypted at the application level before they are written to the database.
Data in Transit: TLS 1.2+
What "data in transit" means
Data in transit is any data moving across a network — between a client and a server, between two microservices, or between your system and a third-party API. If it crosses a network boundary, it must be encrypted.
This includes:
- Browser to API (HTTPS)
- Mobile app to API (HTTPS)
- Internal microservice-to-microservice calls
- Webhooks sent or received
- Database connections from application servers
- Calls to third-party APIs (Twilio, Stripe, etc.)
The requirement
All traffic must use TLS 1.2 at minimum. TLS 1.3 is preferred where supported.
| Version | Status |
|---|---|
| TLS 1.3 | Preferred — faster handshake, stronger defaults |
| TLS 1.2 | Allowed — configure strong cipher suites only |
| TLS 1.1 | Forbidden |
| TLS 1.0 | Forbidden |
| SSL (any version) | Forbidden |
Configuration: Load Balancer / Cloudflare
Most teams should enforce TLS at the edge, not in application code.
Cloudflare:
- Set SSL/TLS mode to Full (Strict).
- Under TLS settings, set Minimum TLS Version to
TLS 1.2. - Enable TLS 1.3 (it is a toggle in the dashboard).
- Enable HSTS with a minimum
max-ageof 1 year.
AWS Application Load Balancer:
- Use the
ELBSecurityPolicy-TLS13-1-2-2021-06security policy or newer. - This policy allows TLS 1.2 and 1.3 and disables all weaker versions.
- Do not use the
ELBSecurityPolicy-2016-08legacy policy — it permits TLS 1.0.
Internal microservices:
- Use mutual TLS (mTLS) for service-to-service communication if your infrastructure supports it (e.g., a service mesh like Istio or AWS App Mesh).
- At minimum, all internal HTTP calls must go over HTTPS — not plain HTTP, even inside a private VPC.
Architectural warning: "We are behind a VPC so it is safe" is not an acceptable reason to use plain HTTP between services. Internal network compromise (a misconfigured security group, a compromised container) exposes all unencrypted traffic in the same network. Encrypt in transit everywhere.
Database connections
Every connection string to a database must include TLS. Example for PostgreSQL:
// ✅ Correct — TLS required
const client = new Client({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: true }, // validates the server certificate
});
// ❌ Wrong — disables certificate validation
const client = new Client({
connectionString: process.env.DATABASE_URL,
ssl: { rejectUnauthorized: false },
});
rejectUnauthorized: false disables certificate validation. It makes the TLS connection useless because you can no longer detect a man-in-the-middle attack. Do not use it in production, even temporarily.
Forbidden Algorithms
The following algorithms are strictly prohibited for any security-sensitive operation — authentication, hashing, data protection, token generation, or digital signatures. Their prohibition is not a preference: each of these has been formally broken or deprecated by NIST, and real-world attacks against them have been demonstrated publicly.
Using any of these in a new feature is a blocking code review issue. Finding one in existing code is a required remediation item.
Summary table
| Algorithm | Status | Key attack | Use instead |
|---|---|---|---|
| MD5 | Disallowed (NIST SP 800-131A Rev 2) | Wang & Yu collision, 2004 | SHA-256 / SHA-3 |
| SHA-1 | Disallowed for signatures (NIST, 2014) | SHAttered collision, 2017 | SHA-256 / SHA-3 |
| DES | Withdrawn (FIPS 46-3, 2005) | EFF Deep Crack, 22 hrs, 1999 | AES-256 |
| 3DES | Disallowed for new encryption (NIST, 2024) | Sweet32 (CVE-2016-2183) | AES-256 |
| RC4 | Prohibited in TLS (RFC 7465, 2015) | FMS attack / Bar Mitzvah | AES-GCM / ChaCha20 |
| AES-ECB mode | Not approved for general use (NIST SP 800-38A revision) | Structural pattern leak | AES-GCM or AES-CBC |
| TLS 1.0 / 1.1 | Deprecated (RFC 8996, 2021) | BEAST, POODLE | TLS 1.2+ |
| SSL (all versions) | Completely broken | POODLE, DROWN | TLS 1.2+ |
MD5
MD5 is fully broken for any security purpose.
MD5 produces a 128-bit hash and was designed in 1991. Practical collision attacks were first demonstrated by Wang and Yu in 2004 — two different inputs that produce the same MD5 hash can be computed in seconds on consumer hardware. In 2012, the Flame malware used an MD5 collision to forge a fraudulent Microsoft code-signing certificate, allowing it to spread as a trusted Windows update.
NIST disallows MD5 for digital signature generation and HMAC in SP 800-131A Rev 2 (2019).
Prohibited uses: password hashing, digital signatures, HMAC, token integrity, certificate fingerprints.
Allowed uses (non-security only): file download checksums, cache keys, deduplication. If the result has no security implication, MD5 is acceptable. If someone can benefit from a collision, it is forbidden.
// ❌ Never — MD5 for any auth or integrity check
import { createHash } from 'crypto';
const token = createHash('md5').update(secret).digest('hex');
// ✅ Use SHA-256 or SHA-3
const token = createHash('sha256').update(secret).digest('hex');
SHA-1
SHA-1 is broken. Collisions are practical and have been demonstrated publicly.
SHA-1 produces a 160-bit hash. Theoretical vulnerabilities were published by Wang, Yin, and Yu in 2005. On February 23, 2017, researchers from CWI Amsterdam and Google published SHAttered — the first practical SHA-1 collision. They produced two different PDF files with the same SHA-1 hash, making it possible to swap one for the other without changing its fingerprint. The computational cost was approximately $110,000 at the time; it is lower today.
NIST disallowed SHA-1 for new digital signature generation after December 31, 2013 (SP 800-131A). All major browsers stopped trusting SHA-1 TLS certificates in 2017.
Prohibited uses: digital signatures, certificate fingerprints, HMAC, password hashing, token integrity.
Allowed uses (non-security only): same rule as MD5 — non-security checksums only.
// ❌ Never — SHA-1 for signatures or token verification
const sig = createHash('sha1').update(payload).digest('hex');
// ✅ Use SHA-256 minimum; SHA-384 or SHA-512 for signatures
const sig = createHash('sha256').update(payload).digest('hex');
DES
DES is completely broken. Do not use it under any circumstances.
DES uses a 56-bit key, designed in 1977. In January 1999, the Electronic Frontier Foundation's Deep Crack hardware — combined with the Distributed.net volunteer network — cracked a DES-encrypted message in 22 hours and 15 minutes. Modern hardware does it in far less time.
NIST formally withdrew the DES standard (FIPS 46-3) on May 19, 2005. It is listed as disallowed in SP 800-131A.
Replace with: AES-256-GCM.
3DES (Triple DES)
3DES is deprecated and disallowed for new encryption since January 1, 2024.
3DES applies DES three times with different keys, yielding an effective key length of 112 bits. While stronger than DES, it has two critical problems:
-
Sweet32 (CVE-2016-2183): Published by INRIA researchers in August 2016. Because 3DES operates on 64-bit blocks, after approximately 32 GB of data encrypted under the same key, birthday-bound collisions occur. An attacker capturing enough ciphertext can recover plaintext — demonstrated against HTTPS and OpenVPN sessions using 3DES.
-
Performance: 3DES is significantly slower than AES. Choosing it over AES has no benefit on modern hardware.
NIST SP 800-67 Rev 2 (the 3DES specification) was withdrawn on January 1, 2024. SP 800-131A Rev 2 disallows 3DES for new encryption as of that date.
Replace with: AES-256-GCM.
// ❌ Never — 3DES
const cipher = crypto.createCipheriv('des-ede3-cbc', key, iv);
// ✅ AES-256-GCM
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
RC4
RC4 is prohibited in TLS and has multiple exploitable biases.
RC4 is a stream cipher once widely used in WEP and early TLS. Its key scheduling algorithm was broken by Fluhrer, Mantin, and Shamir (FMS attack, 2001), which led to the complete compromise of WEP. In 2015, Itsik Mantin demonstrated the Bar Mitzvah attack (CVE-2015-2808) recovering plaintext from RC4-encrypted TLS sessions.
RFC 7465 (2015) prohibits RC4 cipher suites in TLS. NIST SP 800-52 Rev 2 prohibits all RC4 cipher suites for federal systems.
Replace with: AES-256-GCM (for TLS) or ChaCha20-Poly1305 (natively in TLS 1.3).
AES-ECB Mode
ECB mode does not provide semantic security. It is structurally broken.
Electronic Codebook (ECB) mode encrypts each block of plaintext independently with the same key. Identical plaintext blocks always produce identical ciphertext blocks — the cipher is deterministic and reveals the structure of the underlying data. The canonical demonstration is encrypting a bitmap image with AES-ECB: the encrypted output still clearly shows the original image's silhouette.
ECB has no role in application encryption. NIST is revising SP 800-38A specifically to restrict ECB approval to a narrow set of explicitly permitted uses (such as key wrapping), removing it as a general-purpose mode.
Replace with: AES-256-GCM (provides both encryption and authentication) or AES-256-CBC with a random IV.
// ❌ Never — ECB mode
const cipher = crypto.createCipheriv('aes-256-ecb', key, null);
// ✅ GCM: encryption + integrity in one
const iv = crypto.randomBytes(12);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
Auditing Your Codebase
Run these commands to find forbidden algorithms in existing code before they reach production.
# Find MD5 and SHA-1 usages (Node.js / TypeScript)
grep -rn "createHash\s*(\s*['\"]md5\|createHash\s*(\s*['\"]sha1\|createHash\s*(\s*['\"]sha-1" src/
# Find DES and 3DES cipher usage
grep -rn "des-ede\|des-cbc\|des-ecb\|createCipheriv.*des" src/
# Find ECB mode
grep -rn "ecb" src/ --include="*.ts" --include="*.js" -i
# Find RC4
grep -rn "rc4\|arcfour" src/ --include="*.ts" --include="*.js" -i
# Find weak TLS settings (disabled cert validation)
grep -rn "rejectUnauthorized.*false\|NODE_TLS_REJECT_UNAUTHORIZED" src/
Add these checks to your CI pipeline to catch regressions automatically. A single grep --count returning non-zero should fail the build.
On MD5 and SHA-1 for non-security uses: MD5 and SHA-1 are acceptable for non-security checksums — verifying a file download, generating a cache key, or deduplicating data where collisions have no security consequence. They are forbidden for all security purposes: password hashing, digital signatures, token integrity, HMAC, and authentication.
Quick Reference
✅ Data at rest: AES-256-GCM, fresh IV per call, key from secrets manager
✅ Data in transit: TLS 1.2 (minimum), TLS 1.3 (preferred), HTTPS everywhere
✅ Passwords: bcrypt / argon2 (never encrypt passwords — hash them)
❌ Never use: MD5, SHA-1, DES, 3DES, RC4, ECB mode, SSL, TLS 1.0, TLS 1.1
❌ Never commit encryption keys to git
❌ Never disable rejectUnauthorized in TLS connections
❌ Never reuse an IV
| Task | Tool / Standard |
|---|---|
| Encrypt a field before storing | aes-256-gcm via Node.js crypto |
| Protect data on disk / S3 | Cloud provider encryption (KMS/SSE) |
| Secure API traffic | TLS 1.2+ at load balancer / Cloudflare |
| Secure internal service calls | HTTPS + mTLS where available |
| Store secrets | AWS Secrets Manager or HashiCorp Vault |
| Hash passwords | bcrypt or argon2 — not encryption |
Questions or edge cases not covered here? Open a discussion in the #security channel or reach out to the platform team.