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)

VulnerabilityWhat HappensPrevention
SQL InjectionMalicious SQL executes in databaseParameterized queries
XSS (Cross-Site Scripting)Malicious scripts run in victim's browserOutput encoding, Content Security Policy
CSRFVictim's browser makes unauthorized requestsCSRF tokens, SameSite cookies
Broken AuthenticationWeak passwords, no MFA, session hijackingStrong auth, MFA, secure session management
Sensitive Data ExposurePasswords, credit cards stored in plaintextEncrypt at rest, hash passwords (bcrypt)
Broken Access ControlUser accesses another user's data by changing IDAuthorization checks on every request
Security MisconfigurationDefault passwords, open debug endpointsHardening 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?"

Leave a Comment