Security Is Not an Afterthought
Security gets its own chapter here because it intersects with every other topic. Every layer — networking, databases, caching, communication — has security implications. Bolting on security after the fact is dramatically more expensive (and less effective) than building it in from the start.
Encryption
Encryption in Transit
All data moving between services, between client and server, or across networks should be encrypted. This prevents eavesdropping, man-in-the-middle attacks, and data tampering.
- TLS (HTTPS): Non-negotiable for any client-facing endpoint. Use TLS 1.3 where possible — it's faster (1-RTT handshake) and removes weak cipher suites.
- mTLS: Mutual TLS for service-to-service communication. Both the client and server present certificates, so each side knows who it's talking to. Common in service mesh architectures (Istio, Linkerd).
- Certificate management: Use automated certificate rotation (Let's Encrypt, AWS Certificate Manager). Manually managed certs will expire at the worst possible time.
Encryption at Rest
Data stored on disk — databases, file systems, backups — should be encrypted so that physical access to the storage medium doesn't expose the data.
- Full-disk encryption: Encrypts the entire volume. Transparent to applications. Available on all major cloud providers by default.
- Application-level encryption: Encrypt sensitive fields (SSN, credit card numbers) before writing to the database. Even if the database is compromised, the sensitive data remains encrypted.
- Key management: Don't store encryption keys alongside the encrypted data. Use a dedicated key management service (AWS KMS, HashiCorp Vault) with strict access controls and audit logging.
Input Validation & Sanitization
Never trust input from outside your system boundary. Every piece of external data — user forms, API parameters, webhook payloads, file uploads — should be validated and sanitized.
Common Attack Vectors
| Attack | What happens | Prevention |
|---|---|---|
| SQL Injection | Attacker inserts SQL code via input fields | Parameterized queries. Never concatenate user input into SQL strings. |
| XSS | Attacker injects JavaScript into your pages | Escape all user-generated content. Use CSP headers. Sanitize HTML input. |
| CSRF | Attacker tricks users into making unintended requests | CSRF tokens, SameSite cookies, verify Origin header. |
| Path Traversal | Attacker accesses files outside intended directory | Sanitize file paths. Never use user input directly in file system operations. |
| SSRF | Attacker makes your server send requests to internal services | Allowlist permitted URLs. Block requests to internal IP ranges. |
Principle of Least Privilege
Every user, service, and process should have only the minimum permissions needed to do its job — and nothing more. If your API server only reads from the users table, its database credentials should only have SELECT on that table, not DROP TABLE on the entire database.
Applying Least Privilege
- Service accounts: Each microservice gets its own credentials with narrowly scoped permissions. No shared "admin" accounts.
- IAM roles: Use cloud IAM roles instead of long-lived API keys. Roles provide temporary credentials that auto-rotate.
- Network segmentation: Services that don't need to talk to each other shouldn't be able to. Use security groups, network policies, or service mesh authorization.
- Secrets management: Never hardcode secrets in code or config files. Use a secrets manager (Vault, AWS Secrets Manager, GCP Secret Manager) with audit trails.
Defense in Depth
No single security measure is foolproof. Defense in depth means layering multiple controls so that if one fails, others still protect you:
Authentication & Authorization
Authentication answers "who are you?" Authorization answers "what are you allowed to do?" They're separate concerns that are often conflated.
- Authentication patterns: Session-based (server stores session state), JWT tokens (stateless, signed tokens that encode user identity), OAuth 2.0 (delegated authorization for third-party access).
- Authorization patterns: RBAC (role-based — admin, editor, viewer), ABAC (attribute-based — more flexible rules like "user can edit documents they created"), ACL (access control lists — per-resource permissions).
Rate Limiting
Rate limiting controls how many requests a client can make in a given time window. It protects against abuse, brute-force attacks, and accidental overload.
- Fixed window: Allow N requests per minute. Simple but susceptible to bursts at window boundaries.
- Sliding window: Smooths out bursts by tracking requests over a rolling time period.
- Token bucket: Tokens are added at a fixed rate. Each request consumes a token. Allows controlled bursts while maintaining an average rate.
- Leaky bucket: Requests are processed at a fixed rate. Excess requests queue up (or are dropped). Provides very consistent output rate.