Part 1 of 5
Authentication vs. Authorization
Two words that get used almost interchangeably in casual conversation, and confusing them is the root of a huge share of real security bugs.
1.1 The Actual Distinction
Authentication answers "who are you?" Authorization answers "what are you allowed to do?" - and they fail in completely different ways.
Authentication (AuthN)
- Proves identity - you are who you claim to be
- Happens once, typically at login
- Example: entering your password, scanning your fingerprint, an SSO redirect completing
VS
Authorization (AuthZ)
- Governs permission - what an already-identified user can touch
- Checked on every request that touches something sensitive
- Example: you're logged in, but can you view this specific invoice, or only your own?
🌍 Real-World Example
Logging into your bank's app is authentication. Being blocked from viewing another customer's account balance - even though you're a fully logged-in, legitimate user of the app - is authorization. A system can get the first exactly right and still leak every customer's data through the second.
⚠️ Common Misconception
"We check permissions in the UI, so we're covered." Hiding a button isn't authorization - it's decoration. If the API endpoint behind that button doesn't independently check the same permission, anyone with a browser's dev tools can call it directly.
1.2 Common Authentication Mechanisms
Different ways to prove "it's really me," each with a different trust model
Password + Hash
OAuth / SSO
API Keys
MFA / Biometrics
mTLS (service-to-service)
🛠️ System-Design Implementation
A typical public API: OAuth/SSO for human users logging into a web app, API keys or mTLS for service-to-service and third-party integrations, and MFA layered on top of passwords for anything sensitive. Rarely just one - real systems stack these by context.
1.3 Role-Based Access Control (RBAC) & Permissions
You don't assign permissions to people - you assign roles, and roles carry permissions (looping)
How it actually works
A roleA named bundle of permissions - "editor," "admin," "viewer" - assigned to users instead of managing each permission per-person. is just a named bundle of permissions. A user gets one or more roles; the role, not the user record, is what's checked against a resource. Change what "editor" can do once, and every editor updates instantly.
Common security pitfalls
- Checking the role client-side only, never re-verifying server-side
- Roles that silently accumulate permissions over time ("permission creep") with no periodic review
- One giant "admin" role instead of granular, purpose-built roles
🎯 Interview Perspective
Q: Design an access-control system for a multi-tenant SaaS product.Roles scoped per-tenant (an "admin" in Company A has zero authority in Company B), permission checks enforced at the API/service layer - never trusting a client-supplied role - and a background job that flags roles nobody's touched in 90 days, since unused permissions are exactly what attackers look for first.
Part 2 of 5
Authentication & JWT
The most common way modern APIs prove "you already logged in" on every single request, without asking again - and everything that can go wrong with it.
2.1 Session-Based vs Token-Based Authentication
Where "who's logged in" actually lives
Session-Based
- Server stores session state; client just holds a session ID cookie
- Revoking access is instant - delete the server-side session
- Doesn't scale horizontally without a shared session store
VS
Token-Based (JWT)
- The token itself carries the identity - the server checks a signature, not a database
- Stateless - any server instance can verify it, no shared store needed
- Revoking a single token before it expires is genuinely hard (below)
2.2 JWT Structure: Header, Payload, Signature
Three parts, dot-separated, and only one of them is actually secret (looping)
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyXzQ0MiJ9.4a7f9c...signature
⚠️ Common Misconception
The payload is base64-encoded, not encrypted. Anyone who intercepts a JWT can decode the header and payload instantly and read every claim in plain text. The signature only proves the token wasn't tampered with - it does nothing to keep the payload's contents secret. Never put a password or sensitive secret in a JWT payload.
2.3 Access Tokens, Refresh Tokens & the Token Lifecycle
A short-lived token you use constantly, backed by a longer-lived one you use rarely (looping)
Secure storage & rotation
- Access tokens: short-lived (minutes), kept in memory where possible - never
localStorageif avoidable, since JS-readable storage is exactly what XSS steals - Refresh tokens: longer-lived, stored in an
HttpOnly,Securecookie - invisible to JavaScript entirely - Rotation: issue a brand-new refresh token every time one is used, and invalidate the old one - a stolen, already-used refresh token becomes worthless
Common JWT vulnerabilities
- alg:none - some libraries once accepted a token claiming "no signature" as valid; always allowlist accepted algorithms explicitly
- No expiration - a token that never expires is a permanent skeleton key if it ever leaks
- No real revocation - stateless by design, so a "logout" doesn't invalidate an already-issued token unless you track something server-side (a denylist, or a short enough expiry that it barely matters)
2.4 End-to-End Authentication Flow
Login once, then the token does the talking on every request after that (looping)
🎯 Interview Perspective
Q: Why can't you just "log a JWT out" server-side?Because the whole point of a JWT is that no server round-trip is needed to verify it - there's no database row to delete. Real systems handle this with short access-token lifetimes plus a refresh-token store that can be revoked, so "logout" invalidates the refresh token and just waits out the access token's short remaining life.
Part 3 of 5
Data Encryption
Authentication and authorization decide who's allowed to see data. Encryption is the backstop for when someone sees it anyway - over the wire, on a stolen disk, or in a leaked database dump.
3.1 Encryption in Transit vs At Rest
Data is vulnerable while moving and while sitting still - for different reasons, with different fixes
In Transit
- Protects data while it travels across a network - TLS/HTTPS is the standard answer
- Defends against interception: someone reading traffic on the wire
- Applies between client↔server, and increasingly service↔service too
VS
At Rest
- Protects data while it's stored - on disk, in a database, in a backup
- Defends against a stolen disk, a misconfigured backup bucket, or raw filesystem access
- Encrypted disks/DBs are useless if the running application still hands out plaintext to anyone who asks - it's a backstop, not a substitute for access control
🌍 Real-World Example
A cloud provider encrypts every EBS volume and S3 bucket at rest by default - but a public, misconfigured S3 bucket with encryption enabled still leaks every file to anyone with the URL. Encryption at rest defends against someone stealing the disk, not against someone being handed the door key.
3.2 TLS/HTTPS - How the Handshake Actually Works
A few round trips to agree on a shared secret, then everything after that is fast and symmetric (looping)
Deep dive: why TLS uses both asymmetric and symmetric encryption in the same handshake
Asymmetric encryption (below) is secure without a shared secret in advance, but it's computationally expensive - too slow to encrypt every byte of real traffic. So TLS uses asymmetric crypto for exactly one job: safely agreeing on a temporary symmetric key without ever sending that key in the clear. Once both sides have it, the rest of the session uses fast symmetric encryption. Best of both: no pre-shared secret needed, and fast enough for real traffic.
3.3 Symmetric vs Asymmetric Encryption
One key that must stay secret between two parties, or two keys where only one ever does
Symmetric (e.g. AES)
- One key encrypts and decrypts
- Fast - used for the bulk of real traffic and data at rest
- The hard problem: both sides need the same key, safely, in advance
VS
Asymmetric (e.g. RSA)
- A public key encrypts, only the matching private key decrypts
- Solves the key-distribution problem - the public key is safe to hand out openly
- Much slower - not used for bulk data, just for exchanging a symmetric key or signing something
3.4 Hashing vs Encryption - and Why Passwords Get Hashed, Not Encrypted
Encryption is reversible on purpose. Hashing is designed never to be (looping)
Encryption keys & key management
A key that encrypts a database is itself a secret that needs protecting - which is what a KMSKey Management Service - a dedicated, hardened system (or hardware) whose entire job is generating, storing, and rotating encryption keys safely. or HSM is for. Envelope encryption (encrypt the data key with a master key, then that data key encrypts the actual data) means rotating the master key never requires re-encrypting all your data.
Where encryption shows up in a real system
- TLS between every client↔service and service↔service hop
- Database-level encryption at rest (transparent data encryption)
- Application-level field encryption for the most sensitive columns (SSNs, payment details)
- Encrypted backups and snapshots, with keys managed separately from the data itself
⚠️ Common Misconception
"We encrypt passwords before storing them." You shouldn't - encryption is reversible by design, which means anyone with the key (or who steals it) can recover every plaintext password at once. Passwords get hashed and salted instead: one-way, and a unique salt per password means even two identical passwords produce completely different stored hashes.
Part 4 of 5
Rate Limiting & Throttling
Authentication proves who you are. Rate limiting decides how much of you the system can actually survive - deliberate or not.
4.1 Why Rate Limiting Exists - and Rate Limiting vs Throttling
Two related, often-confused controls with genuinely different responses
Rate Limiting
- A hard ceiling - once you're over it, requests are rejected with
HTTP 429 Too Many Requests - Protects finite resources, and defends against abuse and brute-force attempts
VS
Throttling
- Deliberately slows requests down instead of rejecting them - added delay, not a hard stop
- Smooths a burst into a manageable pace rather than turning traffic away outright
4.2 The Four Core Algorithms
Fixed window's boundary flaw, solved two different ways (looping)
Fixed WindowSimple counter per time bucket. The boundary between two windows can let roughly double the real limit through.
Sliding WindowCounts the actual trailing window in real time, not a fixed clock tick. No boundary flaw, a bit more bookkeeping.
Token BucketRefills steadily, spends in bursts up to the bucket size. The most commonly reached-for default.
Leaky BucketQueues requests and drains them at a constant rate. Smoothest output, adds latency under load.
4.3 Distributed Rate Limiting with Redis, at the API Gateway
Three app servers, one shared counter - or the limit doesn't actually mean anything (looping)
⚠️ Common Misconception
"Each server tracks its own counter, so 3 servers behind a load balancer with a limit of 100 gives you 100 total." It gives you up to 300 - each server's local counter has no idea the other two exist. A shared, atomic counter (Redis INCR with a TTL) is what makes the limit actually global, and enforcing it once at the API Gateway (topic: Load Balancer & API Gateway) means every service behind it is protected without repeating the logic everywhere.
🌍 Real-World Example
A public API charges per request tier and enforces limits per API key using Redis counters with a sliding window, returning 429 with a Retry-After header - exactly the practical trade-off of "reject clearly and quickly" over silently degrading.
Part 5 of 5
Secure Design Best Practices
Everything above is a mechanism. This part is the judgment about when and how much of each mechanism to actually use.
5.1 Core Principles
Four ideas that show up underneath almost every other security decision
Least PrivilegeEvery user, service, and key gets the minimum access it needs - nothing "just in case."
Defense in DepthLayer independent controls, so one failure doesn't mean total compromise.
Zero TrustNever trust a request just because it's "inside the network" - verify every single time, from anywhere.
Secure DefaultsThe out-of-the-box configuration should be the safe one - opting into risk should be deliberate, not accidental.
🛠️ System-Design Implementation
Input validation & sanitization: validate on the server, always - client-side checks are UX, not security. Secrets management: a dedicated vault (not environment variables committed to git, not hardcoded strings), with automatic rotation. API security: authentication, rate limiting, and input validation on every single endpoint, not just the "important" ones. Dependency & supply-chain security: automated scanning for known vulnerabilities, lockfiles pinned to exact versions, and a real process for patching when a CVE drops.
5.2 Common Attacks vs Their Defenses
The same pattern every time: untrusted input treated as trusted - attack blocked live (looping)
| Attack | What it exploits | The defense |
|---|---|---|
| SQL Injection | User input concatenated directly into a query | Parameterized queries / prepared statements - never string-build SQL |
| XSS (Cross-Site Scripting) | Untrusted input rendered as HTML/JS in someone else's browser | Output encoding, a strict Content-Security-Policy header |
| CSRF (Cross-Site Request Forgery) | A browser auto-sending your cookies to a request you didn't intend | CSRF tokens, SameSite cookies |
| SSRF (Server-Side Request Forgery) | Tricking your server into making requests to internal systems on your behalf | Allowlist outbound destinations, block requests to internal IP ranges |
| Replay Attacks | Capturing a valid request and resending it later | Nonces, timestamps, and short-lived signed requests |
5.3 Security Headers, CORS & Operating It Safely
The parts of security that are less about code and more about configuration and habits
Headers & CORS
Content-Security-Policy,X-Content-Type-Options,Strict-Transport-Security- cheap, high-value headers most APIs skip- CORS isn't a security feature for your server - it protects browsers from your API being called by pages you didn't intend; your API still needs its own auth checks
Secure microservices & observability
- Service-to-service traffic gets mTLS, not an implicit "it's internal, so it's trusted"
- Log security-relevant events (auth failures, permission denials) - but never log secrets, tokens, or full card numbers
- Fail closed on errors: an auth check that throws an exception should deny access, not silently allow it
Wrap-Up
Checklist, Cheat Sheet & Interview Questions
Everything above, compressed for the moment you actually need it fast.
🎯 Common Interview Questions
Beyond the per-topic questions above - the ones that test the whole picture at once
"Design an authentication system for a public API."Cover identity (OAuth/API keys), token type and lifetime, refresh strategy, rate limiting per key, and what happens on a suspected key leak.
"How would you secure service-to-service communication in a microservices architecture?"mTLS for identity between services, short-lived service tokens, network policies as a second layer - defense in depth, not one control doing everything.
"A user reports their account was compromised - walk me through your incident response."Revoke all active sessions/tokens immediately, force a password reset, check audit logs for what the attacker actually touched, and only then investigate root cause.
"How do you prevent a single leaked API key from being catastrophic?"Scope every key to the minimum permissions it needs (least privilege), short expiries with rotation, and per-key rate limits so a leaked key can't be used to exfiltrate everything at once.
✅ Security Checklist for System Design Interviews
Say these out loud, unprompted, and most interviewers notice
Authentication and authorization are checked as two separate, explicit steps
Every permission check happens server-side, never trusting the client
TLS everywhere - client↔server and service↔service
Passwords are hashed and salted, never encrypted or stored plain
Rate limiting exists at the edge, backed by a shared, distributed counter
Secrets live in a vault, not in code or environment files committed to git
All user input is validated and parameterized, never concatenated into a query
Tokens are short-lived, with a real rotation and revocation story
Errors fail closed, and logs never contain secrets or full sensitive data
Dependencies are scanned, pinned, and patched on a real cadence
📋 Final Cheat Sheet - Quick Revision
The whole guide, one line per idea
| Topic | The one thing to remember |
|---|---|
| AuthN vs AuthZ | Authentication is who you are; authorization is what you can touch - check both, separately, every time |
| JWT | Signed, not encrypted - never put secrets in the payload; short-lived access token + revocable refresh token |
| Encryption | TLS in transit, encryption at rest for the disk, hashing (never encryption) for passwords |
| Rate Limiting | A shared, distributed counter (Redis) is the only version that actually works across multiple servers |
| Best Practices | Least privilege plus defense in depth - assume any single layer will eventually fail |
🏁 Security in system design isn't one big decision - it's dozens of small, deliberate ones, each closing a specific door. The systems that get breached are rarely missing an exotic control; they're usually missing one of the boring, basic ones, everywhere at once.