Security in System Design
Security in system design means building protections into the architecture from the ground up, not adding them as an afterthought. Every component — APIs, databases, networks, services — must consider how attackers might exploit it and what controls prevent that exploitation. A well-designed system treats security as a core functional requirement, not an optional add-on.
Think of a bank vault. The vault does not just have a strong door — it has motion sensors, security guards, audit logs, and a restricted access room. Multiple layers work together so that bypassing one does not compromise everything. This is defense in depth.
Defense in Depth
Defense in depth means applying multiple independent security layers so that compromising one layer does not give an attacker full access. Each layer stops different attacks.
Layer 1: Firewall → Blocks unauthorized network traffic Layer 2: WAF → Blocks malicious HTTP requests Layer 3: Authentication → Verifies who is making requests Layer 4: Authorization → Controls what they can access Layer 5: Input Validation → Blocks malicious data Layer 6: Encryption → Protects data in transit and at rest Layer 7: Audit Logging → Records all actions for investigation Layer 8: Monitoring/Alerts → Detects unusual activity in real time
Authentication vs Authorization
Authentication (Who are you?)
Authentication verifies the identity of a user or system. It answers: "Is this person really who they claim to be?"
Methods: - Password: Something the user knows - OTP/2FA: Something the user has (phone) - Biometric: Something the user is (fingerprint) - Certificate: Cryptographic proof of identity - API Key: Machine-to-machine identity token
Authorization (What can you do?)
Authorization determines what an authenticated identity is permitted to do. It answers: "Now that we know who you are, what actions are you allowed to perform?"
Example: User: Alice (authenticated as a Marketing Manager) Can Alice: - View reports? YES (Marketing role has read access) - Create campaigns? YES (Marketing role has create access) - Delete user accounts? NO (Only Admin role can delete users) - View salary data? NO (Only HR role can view payroll)
OAuth 2.0 and OpenID Connect
OAuth 2.0 is the standard for delegated authorization — allowing a third-party application to access a user's resources without the user sharing their password.
Flow: "Login with Google" 1. User clicks "Login with Google" on Example App 2. Example App redirects to Google's login page 3. User enters Google credentials (directly with Google, not with Example App) 4. Google asks: "Allow Example App to read your profile?" 5. User clicks Allow 6. Google sends a short-lived code to Example App 7. Example App exchanges code for an access token 8. Example App uses access token to read user's name/email from Google 9. User is logged in — Example App never saw the password! Key Point: The access token has limited scope and expires.
JWT (JSON Web Token) Deep Dive
JWT is the most widely used format for stateless authentication tokens. A JWT contains three base64-encoded sections separated by dots.
JWT Structure: header.payload.signature
Example JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.eyJ1c2VySWQiOjQyLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3MzU2OTAwMDB9
.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Decoded:
Header: { "alg": "HS256", "typ": "JWT" }
Payload: { "userId": 42, "role": "admin", "exp": 1735690000 }
Signature: HMAC-SHA256(header + "." + payload, secretKey)
The signature is verified by the server on every request.
If the payload is tampered with, the signature no longer matches → Rejected.
HTTPS and TLS
All data traveling over the internet must be encrypted. HTTPS uses TLS (Transport Layer Security) to encrypt data in transit, preventing eavesdropping and man-in-the-middle attacks.
Without HTTPS (HTTP):
Browser → [request visible on network] → Server
Password "mysecret" travels as plain text → Anyone intercepting the network sees it!
With HTTPS (TLS):
Browser ←→ TLS Handshake ←→ Server
↓
Both sides agree on encryption keys
↓
Browser → [encrypted data] → Server
"mysecret" travels as "a9f2b3c7..." → Interceptor sees only gibberish
TLS also verifies server identity (certificate):
→ Prevents connecting to a fake server impersonating the real one
In system design: All internal service-to-service communication should also use TLS (mTLS — mutual TLS), not just external traffic. Zero Trust architecture requires every connection to be authenticated and encrypted.
SQL Injection and Input Validation
SQL injection is one of the most common and dangerous attacks. An attacker injects malicious SQL code through user input, tricking the database into executing unintended commands.
Vulnerable Code (Python example): query = "SELECT * FROM users WHERE email = '" + user_input + "'" Attacker enters: ' OR '1'='1 Resulting query: SELECT * FROM users WHERE email = '' OR '1'='1' → Returns ALL users! Attacker bypasses login! More dangerous: DROP TABLE users; -- → Deletes the entire users table! Prevention: Parameterized Queries (Prepared Statements) query = "SELECT * FROM users WHERE email = ?" execute(query, [user_input]) → Database treats input as data, never as SQL code → Attack impossible
Common Security Vulnerabilities (OWASP Top 10)
| Vulnerability | What Happens | Prevention |
|---|---|---|
| SQL Injection | Malicious SQL executes in database | Parameterized queries |
| XSS (Cross-Site Scripting) | Malicious scripts run in victim's browser | Output encoding, Content Security Policy |
| CSRF | Victim's browser makes unauthorized requests | CSRF tokens, SameSite cookies |
| Broken Authentication | Weak passwords, no MFA, session hijacking | Strong auth, MFA, secure session management |
| Sensitive Data Exposure | Passwords, credit cards stored in plaintext | Encrypt at rest, hash passwords (bcrypt) |
| Broken Access Control | User accesses another user's data by changing ID | Authorization checks on every request |
| Security Misconfiguration | Default passwords, open debug endpoints | Hardening checklists, automated config scans |
Encryption: At Rest and In Transit
Encryption In Transit
Protects data as it moves between systems. TLS/HTTPS encrypts data on the wire so interceptors read nothing useful.
Encryption At Rest
Protects data stored on disk. If an attacker steals the physical hard drive or gains unauthorized database access, encrypted data remains unreadable without the decryption key.
Database storage (at rest):
Unencrypted: { "ssn": "123-45-6789", "creditCard": "4111111111111111" }
Encrypted: { "ssn": "a3f9b2...", "creditCard": "7c2d4e..." }
Encrypted data is useless without the encryption key.
Keys are stored separately in a Key Management System (KMS).
Cloud KMS examples: AWS KMS, Google Cloud KMS, Azure Key Vault
Password Security
Passwords must never be stored in plaintext. Always use a strong one-way hashing algorithm with salt.
Bad: Store "mysecret" → Database has "mysecret" → Breach exposes all passwords Bad: MD5 hash → "5ebe2294ecd0e0f08eab7690d2a6ee69" → Reversible with rainbow tables Good: bcrypt with salt: "mysecret" + random_salt → bcrypt → "$2b$12$8oWI2F..." → Even identical passwords produce different hashes (unique salt per user) → bcrypt is intentionally slow (100ms per hash) → brute force takes forever Rule: Use bcrypt, Argon2, or scrypt. Never use MD5, SHA-1, or unsalted SHA-256 for passwords.
Secrets Management
Credentials like database passwords, API keys, and encryption keys must never be hardcoded in source code or committed to version control.
Wrong approach:
db_password = "supersecretpassword123" # In code file
API_KEY = "sk_live_abc123" # Committed to GitHub → EXPOSED!
Correct approach:
db_password = os.getenv("DB_PASSWORD") # From environment variable
API_KEY = vault.get_secret("api_key") # From secrets manager
Tools: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault
Network Security Layers
Public Internet
|
+-----+------+
| Firewall | Blocks unwanted IP ranges, ports
+-----+------+
|
+-----+------+
| WAF | Blocks malicious HTTP (SQL injection, XSS)
+-----+------+
|
+-----+------+
| API Gateway| Auth, rate limiting, TLS termination
+-----+------+
|
Internal Network (Private Subnet - no direct internet access)
|
+-----+------+ +----------+ +----------+
| App | | Database | | Cache |
| Servers | | (Private)| | (Private)|
+------------+ +----------+ +----------+
The database and cache servers live in a private subnet with no direct internet access. Only the application servers (in a DMZ or private subnet) can reach them.
Audit Logging
All security-sensitive actions must be logged with sufficient detail to investigate incidents after the fact.
Log entry format:
{
"timestamp": "2024-01-15T10:30:00Z",
"userId": 42,
"action": "DELETE_USER",
"targetId": 99,
"sourceIP": "192.168.1.5",
"result": "SUCCESS",
"userAgent": "Mozilla/5.0..."
}
Log these events:
- All authentication attempts (success and failure)
- All authorization failures
- All administrative actions
- All data access for sensitive records
- All configuration changes
Summary
Security in system design requires layers — no single control is sufficient. Authentication confirms identity. Authorization enforces what each identity can do. Encryption protects data in transit and at rest. Input validation prevents injection attacks. Secrets management keeps credentials out of source code. Audit logging ensures accountability. Every component in a system architecture needs a threat model: "What can go wrong here, and what control prevents it?"
