TOPIC 11

Security Essentials

Security isn't a feature you bolt on at the end — it's a property of every layer. The fundamentals every system designer must get right, from TLS to OWASP.

Intermediate 35 min read 12 concepts

At a Glance

Encryption: In Transit and At Rest

In transit: plain HTTP is a postcard — every router, Wi-Fi hotspot, and ISP between client and server can read and modify it. TLS (the S in HTTPS) wraps the connection in encryption and authentication: the server presents a certificate, signed by a chain of certificate authorities your browser already trusts, proving "I really am api.shop.dev." Then both sides agree on keys (modern TLS 1.3 does this in one round trip) and everything after is encrypted gibberish to onlookers.

At rest: disks get stolen, backups leak, snapshots get misconfigured. Encrypting stored data — full-disk encryption, database-level encryption (TDE), or per-field application-level encryption with AES-256 — means a stolen disk is a stolen brick. Cloud providers make this nearly free (S3/EBS/RDS encryption is a checkbox); the real work is key management (KMS, Vault) and never committing keys to git.

Client Server TLS 1.3 sees only: 8f3ax0…¤q2 🤷

With TLS, the eavesdropper still sees packets — just not anything useful inside them.

TLS Handshake — Step by Step

What actually happens in that "one round trip" before your data is safe? The TLS 1.3 handshake, simplified:

1
ClientHello

The client sends its supported cipher suites, TLS version, and a random nonce — plus its own key-share guess to save a round trip.

2
ServerHello

The server picks a cipher suite and sends its certificate, which contains the server's public key.

3
Key exchange

Client and server derive session keys via ECDHE using the exchanged key shares; the certificate's private key proves the server's identity — only the real key holder can complete the exchange.

4
Symmetric from here on

Both sides now hold the same session key. All further traffic is symmetrically encrypted (AES-GCM) — fast bulk encryption, asymmetric crypto only at setup.

5
1-RTT (and 0-RTT)

TLS 1.3 completes all of this in one round trip, and resumed sessions can send data with zero round trips (0-RTT).

Trust, Pinning, and HSTS

Authentication vs Authorization

Two words, constantly confused, never interchangeable:

A 401 means "I don't know who you are"; a 403 means "I know exactly who you are, and no."

Session-based auth (stateful)

On login, the server creates a session record (in Redis or the DB) and hands the browser a random session ID cookie. Every request looks the session up server-side. Revocation is trivial — delete the record — but every request costs a lookup, and all servers must share the session store.

Token-based auth: JWT (stateless)

A JSON Web Token packs the user's identity and claims into a signed blob: header.payload.signature. Any server holding the public key can verify it without any lookup — perfect for microservices and APIs. The trade-offs are sharp though:

SessionsJWTs
StateServer-side storeSelf-contained, stateless
Verification costLookup per requestSignature check (no I/O)
Instant revocation delete the sessionwait for expiry (or keep a denylist… now you're stateful again)
Best forClassic web appsAPIs, microservices, mobile

JWT — Structure, Security, Pitfalls

Cracking open the token: a JWT is three base64url-encoded parts joined by dots — Header.Payload.Signature:

// Header
{"alg": "HS256", "typ": "JWT"}
// Payload
{"sub": "user:123", "email": "alice@example.com", "iat": 1640000000, "exp": 1640086400}
// Signature
HMACSHA256(base64url(header) + "." + base64url(payload), secret)

The server verifies the signature without any database lookup — that statelessness is the whole appeal for scaling. But the pitfalls are real:

!
Base64 is not encryption

Never store sensitive data in a JWT payload. It's base64-encoded, not encrypted — anyone holding the token can decode and read every claim in one line of code.

OAuth 2.0 & OpenID Connect

OAuth 2.0 answers: how does app A access your data in app B without ever seeing your app-B password? It's a framework for delegated authorization — the "Sign in with Google" and "let this app read your calendar" machinery. Key flows:

1
Authorization Code (+ PKCE)

The main flow. The user approves at the provider, the app receives a one-time code, and exchanges it server-side for an access token. PKCE protects the exchange for mobile/SPA clients. Use this one.

2
Client Credentials

No user involved — service A authenticates as itself to call service B. The machine-to-machine flow.

3
Implicit (deprecated)

Tokens delivered straight in the browser URL fragment. Leaky; replaced by Code + PKCE. Mentioning that it's deprecated is itself an interview point.

OpenID Connect (OIDC) is a thin identity layer on top of OAuth: it adds a signed id_token (a JWT) that asserts who the user is — turning OAuth's authorization machinery into a proper authentication protocol. OAuth = what you may access; OIDC = who you are.

OAuth 2.0 Authorization Code Flow — Walkthrough

Four roles to keep straight: the Resource Owner (the user), the Client (your app), the Authorization Server (Google/GitHub/Okta), and the Resource Server (the API holding the data). The Authorization Code flow — the most secure option for web apps — step by step:

1
User clicks "Login with Google"

Your app needs identity or data from Google — without ever seeing the user's Google password.

2
Redirect to the authorization server

https://accounts.google.com/o/oauth2/v2/auth?client_id=...&redirect_uri=...&scope=email profile&response_type=code&state=xyz — the state value protects against CSRF on the way back.

3
User authenticates and grants permission

This happens entirely at Google. Your app is not involved — and never sees credentials.

4
Redirect back with a one-time code

https://yourapp.com/callback?code=AUTH_CODE&state=xyz — the code is short-lived and single-use.

5
Exchange code for tokens (server-to-server)

Your backend POSTs to Google's token endpoint with the code and your client_secret — never from the browser, where the secret would leak.

6
Receive tokens

Google returns an access_token (short-lived, ~1 hour) and a refresh_token (long-lived).

7
Use and refresh

The client calls Google APIs with the access token, refreshing via the refresh token when it expires.

PKCE: for clients that can't keep a secret

SPAs and mobile apps can't hold a client_secret — anyone can read their code. PKCE (Proof Key for Code Exchange) fixes this: the client generates a random code_verifier, sends its hash (code_challenge) in step 2, then sends the original verifier in step 5. An attacker who intercepts the authorization code can't redeem it without the verifier.

And the one-line distinction worth repeating: OAuth is for authorization (access to resources); OpenID Connect adds an ID token (a JWT containing user info) on top — that's authentication (who the user is).

Password Storage: One Right Answer

Never plain text. Never reversible encryption. Never fast hashes — MD5 and SHA-1/SHA-256 are designed for speed, so GPUs guess billions per second. The right answer is a slow, salted, adaptive hash: bcrypt, scrypt, or argon2 (current best). Each password gets a unique random salt (killing rainbow tables), and a cost factor keeps hashing expensive (~100 ms) as hardware improves.

# The entire correct implementation:
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash("correct horse battery staple") # salted automatically
ph.verify(hash, attempt) # raises on mismatch

Rate Limiting

Rate limiting protects against brute-force login attempts, scrapers, runaway clients, and cost-amplification attacks. Two classic algorithms:

🪙

Token bucket

A bucket holds up to N tokens, refilled at R/sec; each request spends one. Allows short bursts (a full bucket) while capping the sustained rate. The default choice — Stripe and AWS APIs work this way.

Leaky bucket

Requests enter a queue that drains at a fixed rate; overflow is rejected. Output is perfectly smooth — ideal when downstream must never see spikes.

Return 429 Too Many Requests with a Retry-After header. For distributed limiting across many servers, keep counters in Redis (atomic ops or a small Lua script) — Topic 12 builds exactly this.

Rate Limiting Algorithms — The Full Menu

Token bucket and leaky bucket are the headliners, but interviews often probe the whole family. The trade-offs:

A token bucket against Redis, in full:

def is_allowed(client_id, capacity=100, rate=10):
 key = f"rate:{client_id}"
 now = time.time()
 pipe = redis.pipeline()
 pipe.hget(key, 'tokens')
 pipe.hget(key, 'last_refill')
 tokens, last_refill = pipe.execute()

 tokens = float(tokens or capacity)
 last_refill = float(last_refill or now)
 elapsed = now - last_refill
 tokens = min(capacity, tokens + elapsed * rate)

 if tokens >= 1:
 pipe.hset(key, 'tokens', tokens - 1)
 pipe.hset(key, 'last_refill', now)
 pipe.expire(key, 60)
 pipe.execute()
 return True
 return False

Where to enforce: the API gateway (per client), application code (per user or per endpoint), and the service mesh (per-service limits). Defense in depth applies to throttling too.

Input Validation & the Classic Attacks

The root of most web vulnerabilities is the same sin: treating user input as code.

Round out the hygiene list: HTTPS everywhere with HSTS (browsers refuse to ever try HTTP), cookies flagged Secure; HttpOnly; SameSite, and CORS configured to allow only your real origins — never * with credentials.

OWASP Top 10, the radar view

Injection · broken authentication · sensitive data exposure · XXE (XML external entities) · broken access control (IDOR: change /orders/42 to /orders/43 and read someone else's order — check authorization on every object, not just every route) · security misconfiguration (default creds, open S3 buckets) · XSS · insecure deserialization · components with known vulnerabilities (update your dependencies!) · insufficient logging & monitoring (breaches discovered months late).

Common Vulnerabilities — OWASP Top 10 (2021)

The callout above gave the radar view of the older list; here's the current 2021 edition in full, with what each item actually looks like in the wild:

#VulnerabilityWhat it looks like in practice
1Broken Access ControlA user accesses /admin without the admin role, or another user's data via IDOR.
2Cryptographic FailuresMD5/SHA1 for passwords, secrets transmitted over plain HTTP, weak cipher suites.
3InjectionSQL, LDAP, and command injection. Use parameterized queries ALWAYS.
4Insecure DesignNo threat modeling, missing rate limiting on auth endpoints.
5Security MisconfigurationDefault passwords, unnecessary features enabled, verbose error messages leaking internals.
6Vulnerable & Outdated ComponentsLibraries with known CVEs. Run npm audit, Snyk, or Dependabot.
7Identification & Authentication FailuresBrute force possible, weak passwords allowed, no MFA offered.
8Software & Data Integrity FailuresInsecure deserialization, unsigned CI/CD pipelines.
9Security Logging & Monitoring FailuresNo alerts on failed logins, no audit log for sensitive actions.
10Server-Side Request Forgery (SSRF)A user-controlled URL makes the server request internal resources — like the cloud metadata endpoint.

Zero Trust Architecture

The old model: everything inside the network perimeter is trusted. Get on the VPN and you can access everything. The problem: insider threats, compromised VPN credentials, and lateral movement after any single breach — one popped laptop becomes the whole network.

Zero Trust says: never trust, always verify. Assume breach. Verify every request, regardless of where on the network it comes from.

V

Verify explicitly

Authenticate and authorize every single request — identity, device, and context, every time.

LP

Least privilege

Grant access only to the specific resources needed, for only as long as needed.

AB

Assume breach

Limit blast radius with micro-segmentation — a compromised service can't roam the network.

Implementation in practice: strong identity (MFA, device certificates), service-to-service mTLS via a service mesh, fine-grained authorization with OPA (Open Policy Agent), and audit logging of every access.

Google BeyondCorp

The flagship example: Google employees access internal apps from any network — no VPN — because every request is authenticated via device certificate + user identity + context. The network location simply stopped mattering.

API Security Checklist

Archie says

In a design interview, you rarely get a dedicated security question — you get silent points for weaving it in: "TLS terminates at the load balancer, the gateway validates JWTs and rate-limits per user, passwords are argon2-hashed, and queries are parameterized." Four clauses, big signal.

Interview Q&A

Three security questions interviewers actually ask — click to reveal strong answers.

Use parameterized queries (prepared statements). Never concatenate user input into SQL strings. In Python: cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) — not f"SELECT * FROM users WHERE id = {user_id}". ORMs (SQLAlchemy, Hibernate) use parameterized queries by default. Additionally: a least-privilege DB user (the app user can only SELECT/INSERT, never DROP), a WAF to block obvious attack patterns, and input validation to reject obviously invalid data early.

For most web apps: HttpOnly, Secure, SameSite=Strict cookies holding opaque session IDs stored in Redis. Reasons: XSS cannot steal HttpOnly cookies; SameSite prevents CSRF without extra tokens; the server can instantly revoke sessions (delete from Redis); and no data is exposed (a JWT payload is readable by anyone). JWT shines when: (1) statelessness is required (no shared session store across services), (2) tokens must cross domains or reach mobile apps, (3) you run a short-TTL access token + refresh token flow. Microservices often use JWT internally — service A includes the user's JWT when calling service B, which verifies it without hitting the auth DB. Just keep expiry short (15 min), use refresh tokens, and rotate signing keys regularly.

(1) Authentication: OAuth 2.0 Authorization Code + PKCE for mobile (no client_secret possible); Client Credentials for server-to-server third parties. (2) Authorization: scopes in OAuth tokens (read:users, write:orders), enforced at the API gateway. (3) Rate limiting: per API key, tiered — free at 100/min, paid at 10K/min. (4) Input validation: validate everything, reject early. (5) HTTPS only with an HSTS header. (6) Versioning: /v1/, backward-compatible changes only. (7) Logging: every request with client_id, endpoint, response code, latency — but never request bodies that may contain PII. (8) Secrets rotation: API keys must be rotatable without service disruption.

Anti-Patterns to Avoid

Plaintext or MD5/SHA1 passwords

MD5 and SHA1 are not password hashing algorithms — they're fast, which is exactly wrong for passwords. Use bcrypt (cost 12), Argon2id, or scrypt.

JWTs with no expiry

A stolen token without exp grants permanent access. Always set expiry. For sensitive operations (password change, large transfers), require re-authentication regardless of token freshness.

Secrets baked into Docker images

Image layers are inspectable — secrets leak via docker history. Use proper secret management: Vault, AWS Secrets Manager, or Kubernetes Secrets with external-secrets-operator.

Security Foundations in Distributed Systems

Distributed systems expose dramatically more attack surfaces than monolithic applications — every network boundary, API endpoint, and service-to-service call is a potential entry point. Understanding the foundational principles lets you reason about trade-offs before reaching for any specific tool.

The CIA Triad

Every security decision in system design maps back to three properties:

Threat Modeling

Threat modeling is the structured practice of identifying what you are protecting, who might attack it, where the vulnerabilities lie, and what mitigations are appropriate. Conducted during the design phase of the SDLC (shift-left security), it prevents expensive rework later.

The STRIDE model gives a checklist of threat categories: Spoofing (impersonation), Tampering (data modification), Repudiation (denying actions), Information disclosure (data leakage), Denial of service (availability attacks), and Elevation of privilege (gaining unauthorized permissions). For each component in your design, walk through every STRIDE category and document the mitigation.

Distributed vs Monolithic: Additional Attack Surface

A monolith has one deployment boundary. A distributed system has many: each microservice, message queue, database, cache, CDN edge node, and internal API is an additional entry point. Key implications:

Common Attack Vectors

Understanding the attacker's toolkit helps you design targeted mitigations:

Security in the SDLC

Security must be embedded at every phase rather than bolted on at the end:

Interview Prep

Security Interview Questions

Key questions on security principles, data protection, authentication, and network security.

Data Protection & Secure Communication Questions

Encryption transforms data into unreadable ciphertext that can be reversed (decrypted) with a key. It exists to protect confidentiality. There are two families: symmetric encryption uses the same key for both operations (e.g., AES — fast, suitable for bulk data), and asymmetric encryption uses a public key to encrypt and a private key to decrypt (e.g., RSA — slower, used for key exchange and digital signatures). Encryption is reversible if you hold the key.

Hashing generates a fixed-size, irreversible digest from input data. It is a one-way function — the same input always produces the same output, but you cannot reconstruct the input from the hash. A small change in input produces a completely different hash (the avalanche effect). Hashing is used for integrity checks and password storage (e.g., bcrypt, Argon2, SHA-256 with salt). It is not reversible by design.

Summary: use encryption when you need to retrieve the original data; use hashing when you only need to verify identity or integrity without ever recovering the plaintext.

Asymmetric encryption (RSA, ECC, DSA) relies on computationally hard mathematical problems — for example, RSA uses modular exponentiation over very large primes (2048-bit or 4096-bit keys). These operations are CPU-intensive and cannot be easily hardware-accelerated at the same throughput as symmetric ciphers.

Symmetric encryption (AES) uses a single shared key and operates on data blocks with efficient bitwise and substitution operations. Modern CPUs have dedicated AES-NI instructions that make AES extremely fast — orders of magnitude faster than RSA for the same data volume.

The real-world solution is hybrid encryption: TLS/HTTPS uses asymmetric encryption only during the handshake to securely exchange a symmetric session key, then switches to fast symmetric encryption (AES-GCM) for the actual data stream. This gives you the security properties of asymmetric key exchange with the performance of symmetric encryption.

PKI (Public Key Infrastructure) is a framework of roles, policies, and technologies that enables secure authentication, encryption, and digital signing over untrusted networks like the internet. It answers the critical question: "How do I know this public key really belongs to example.com?"

PKI builds trust through a chain of trust: browsers ship with a set of pre-trusted Root Certificate Authorities (CAs). Root CAs sign Intermediate CA certificates, which in turn sign the leaf certificate for your domain. If the browser trusts the root, it trusts the entire chain down to your certificate. Each digital certificate binds a public key to an identity (domain, organization) and includes the issuer's signature, validity period, and allowed usages.

When a browser visits an HTTPS site, it verifies the certificate's signature, expiration, and issuer against the trusted chain. It also checks revocation status via CRLs (Certificate Revocation Lists) or OCSP (Online Certificate Status Protocol) to catch compromised certificates. PKI ultimately provides: Authentication (you're talking to the real server), Confidentiality (encrypted session), Integrity (tamper detection), and Non-repudiation (signed content cannot be denied).

Data at Rest refers to data stored persistently — on disk, SSDs, databases, file systems, and backups. Threats include physical theft of storage media, compromised database access, and insider threats. Protection methods include full-disk encryption (BitLocker, LUKS), database-level encryption (Transparent Data Encryption / TDE), file-level encryption, and strict IAM roles enforcing least-privilege access. Encryption keys themselves must be managed via a KMS (Key Management Service) rather than stored alongside the data.

Data in Motion (data in transit) refers to data actively moving through the network — between client and server, between microservices, or across data centers. Threats include eavesdropping, MITM attacks, and session hijacking. Protection methods include TLS/SSL (HTTPS for client-server, secure WebSockets), VPNs and IPSec for internal network tunnels, and mutual TLS (mTLS) for service-to-service communication where both sides present certificates.

A robust security posture encrypts data everywhere — at rest and in transit — and combines encryption with strong access controls, auditing, and continuous monitoring. Leaving one state unprotected breaks the entire chain.

Security Focused Questions

Use OAuth 2.0 / OpenID Connect for delegation and identity federation. A dedicated Authentication Server (Auth0, Okta, or a custom implementation) issues short-lived access tokens (~15 min) and longer-lived refresh tokens stored securely server-side or in HttpOnly cookies. Tokens are typically JWTs signed with HMAC-SHA256 or RSA, enabling stateless validation across services without querying a central session store.

Security layers to add on top: enforce HTTPS everywhere so tokens are never transmitted in plaintext; implement MFA for sensitive operations; rate-limit login attempts to prevent brute force; rotate signing keys on a schedule; and set short token TTLs with forced re-authentication for high-risk operations (e.g., password change, large financial transfers). Store minimal session state on the client — only what is needed for the token to be self-describing.

The CIA triad is the foundational security framework: every system design decision that touches security maps to one or more of its three properties.

Confidentiality means preventing unauthorized access to data. In system design: HTTPS for data in transit, AES encryption for data at rest, IAM roles with least privilege, secrets management (Vault, AWS Secrets Manager), and network segmentation via VPCs.

Integrity means preventing unauthorized modification of data. In system design: HMACs on API payloads, digital signatures on critical records, input validation and parameterized queries to prevent injection, immutable audit logs, and database row-level checksums.

Availability means ensuring services are accessible when needed. In system design: load balancing, auto-scaling, multi-region redundancy, DDoS protection (rate limiting, traffic scrubbing), and tested failover/DR procedures.

Important trade-off: these properties can conflict. Maximizing availability (e.g., removing authentication for speed) can compromise confidentiality. Good system design explicitly documents where trade-offs are made and why.

Microservices introduce a large internal attack surface that monoliths avoid. Key threats:

Unauthorized inter-service calls: in a flat internal network, a compromised service can call any other service. Mitigate with mTLS (mutual TLS) or short-lived identity tokens so each service authenticates every incoming call — this is the core of zero-trust networking.

Sensitive data leakage over internal APIs: internal APIs are not automatically safe. Encrypt all internal traffic with TLS and apply schema validation at the API gateway layer.

No centralized audit logging: distributed logs make it nearly impossible to reconstruct an attack timeline. Use a centralized log aggregation platform (ELK, Splunk, Datadog) with tamper-evident storage.

API gateway spoofing: if services blindly trust headers set by a gateway, a compromised internal caller can forge them. Verify JWT signatures at each service, not just at the gateway.

Lateral movement: implement network policies (Kubernetes NetworkPolicy, security groups) so services can only communicate with the specific peers they need. Pair with a service mesh (Istio, Linkerd) for fine-grained traffic control and policy enforcement.

DDoS attacks flood your system with traffic to exhaust bandwidth, compute, or connection capacity. Defense is layered:

Edge-level mitigation: put a CDN or DDoS scrubbing service (Cloudflare, AWS Shield Advanced, Akamai Kona) in front of your origin. These services have massive network capacity to absorb volumetric attacks and can distinguish legitimate traffic from attack traffic using behavioral analysis.

Rate limiting: enforce per-IP and per-user request limits at the API gateway or WAF layer. Combined with CAPTCHAs for interactive endpoints, this raises the cost for attackers significantly.

Auto-scaling: horizontally scale your infrastructure (Kubernetes HPA, AWS Auto Scaling) so sudden traffic spikes do not immediately exhaust capacity while scrubbing kicks in.

Global load balancers: distribute traffic geographically so regional attacks are absorbed without impacting other regions.

Detection: monitor request volume, latency percentiles, and failed authentication rates in real time. Anomaly spikes are the earliest signal of an in-progress attack. Automate runbooks to activate additional protections when thresholds are crossed.

TLS (Transport Layer Security) provides three guarantees for data in transit: confidentiality (the payload is encrypted and unreadable to eavesdroppers), integrity (MAC ensures tampering is detected), and authentication (the server presents a certificate proving its identity).

At scale, certificate management becomes operationally complex. Best practices: use automated certificate rotation via Let's Encrypt with Certbot or ACME protocol integrations — never let certificates expire silently. Offload TLS termination at the load balancer or API gateway (e.g., AWS ALB, Nginx, Envoy) rather than at every application instance, reducing per-service complexity. Enforce HSTS (HTTP Strict Transport Security) so browsers never downgrade to HTTP. Disable deprecated protocol versions (TLS 1.0 and 1.1) and weak cipher suites — enforce TLS 1.2 minimum, preferably TLS 1.3. For internal service-to-service communication, use a service mesh (Istio) that automatically provisions and rotates mTLS certificates via a built-in CA.

Secure cloud data storage requires multiple complementary controls:

Encryption at rest: use cloud KMS-managed keys (AWS KMS, GCP KMS, Azure Key Vault) for all storage services — object stores, databases, and block volumes. Customer-managed keys (CMKs) give you control over key rotation and revocation.

Role-based access control (RBAC): follow the principle of least privilege — each service, function, or user should be granted only the permissions strictly required for its job. Database read replicas used for reporting should not have write access.

Secrets management: never hardcode credentials. Store API keys, database passwords, and certificates in a dedicated secrets manager (AWS Secrets Manager, HashiCorp Vault) with automatic rotation enabled.

Audit logging: enable CloudTrail (AWS), Cloud Audit Logs (GCP), or equivalent so you have an immutable record of who accessed what and when. Alert on anomalous access patterns — unexpected geographic locations, unusually large data exports, or access outside business hours.

Data classification: classify data by sensitivity and apply stricter controls (additional encryption layers, narrower access) to PII, financial data, and secrets.

Threat modeling is a structured process for identifying security risks early. It answers four questions: What are you protecting (assets)? Who might attack it (threat actors)? Where are you vulnerable (attack vectors)? How do you defend (mitigations)?

Integration into the SDLC: conduct threat modeling during the design phase, before implementation begins — changes are cheap at design time and expensive in production. For each major feature or architectural change, assemble the relevant engineers and walk through the system diagram using a framework like STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege) or DREAD (Damage, Reproducibility, Exploitability, Affected users, Discoverability) to score risks.

Document the findings: list every threat, the component it affects, its likelihood and impact, and the chosen mitigation. Revisit the threat model with every significant feature addition or architecture change. Threat modeling is not a one-time exercise — it is an ongoing practice that matures as the system evolves.

Authentication & Authorization Questions

JWT (JSON Web Token) is popular for distributed systems because it is self-contained: a JWT carries all necessary claims — user identity, roles, permissions, and expiration — encoded directly in the token. A receiving service can validate the token by verifying its cryptographic signature (HMAC-SHA256 or RSA) without querying a central session store. This stateless validation is critical for horizontal scalability: any instance of any service can independently validate any token.

Additional advantages: JWTs are compact and URL-safe (transmitted as a Bearer header), they follow a well-known standard (RFC 7519) supported across languages and platforms, and the payload can carry custom claims to avoid extra database lookups in authorization logic.

Key caveats: JWT tokens cannot easily be revoked before expiry without maintaining a token blacklist (which reintroduces statefulness). Store tokens in HttpOnly cookies, not localStorage, to prevent XSS theft. Use short expiration (~15 minutes) with refresh tokens to limit the damage window if a token is stolen. Always validate the signature, iss, aud, and exp claims — never accept tokens with alg: none.

OAuth 2.0 is an authorization framework. It allows a third-party application to obtain limited access to a user's resources on another service — without exposing the user's credentials. The classic example: granting a third-party app permission to post on your Twitter account. OAuth 2.0 issues an access token scoped to specific resources and actions. It says nothing about who the user actually is.

OpenID Connect (OIDC) is an authentication layer built on top of OAuth 2.0. It adds the concept of an ID token (a JWT) that contains verified identity claims about the user — name, email, subject identifier. OIDC standardizes login flows so that a relying party (your app) can verify the user's identity through an identity provider (Google, Okta, Auth0).

In practice: use OAuth 2.0 when you need delegated resource access; use OIDC when you need to authenticate who the user is. Most modern systems use both: OIDC authenticates the user and OAuth 2.0 scopes control what the issued tokens can access.

RBAC (Role-Based Access Control) grants access based on user roles (Admin, Editor, Viewer). A user is assigned one or more roles, and each role maps to a set of permissions. RBAC is simple to implement and audit, but can lead to "role explosion" as the number of distinct permission combinations grows.

ABAC (Attribute-Based Access Control) grants access based on a combination of attributes: user attributes (department, clearance level), resource attributes (classification, owner), and environmental attributes (time of day, IP address, device trust level). Example policy: "A user with department=HR can access payroll records during business hours from a managed device." ABAC policies are expressed in policy engines like OPA (Open Policy Agent) or AWS Cedar.

For a highly dynamic environment — multi-tenant SaaS, cloud platforms, systems with context-sensitive rules — ABAC is more suitable. It handles fine-grained and context-aware decisions that RBAC cannot express without an impractical number of roles. The trade-off is complexity: ABAC policies are harder to reason about and audit than simple role assignments.

Single Sign-On (SSO) allows a user to authenticate once with a central identity provider and then access multiple applications or services without re-entering credentials. Common protocols: SAML 2.0, OAuth 2.0 / OIDC.

Advantages: Improved user experience — one login for all connected systems reduces friction and credential fatigue. Stronger security posture — centralizing authentication allows the organization to enforce consistent security policies (MFA, password complexity, session timeouts) across all applications from a single control plane. Reduced attack surface — fewer passwords means fewer credentials to phish or breach. Simplified compliance and auditing — central authentication logs provide a unified access trail for SOC 2, HIPAA, and other compliance frameworks. Reduced helpdesk load — fewer forgotten-password tickets when users only manage one set of credentials.

SSO also enables federated identity across organizational boundaries (e.g., B2B partnerships), allowing employees of one company to access a partner's systems using their home-company credentials.

Token-based authentication (particularly JWTs) is powerful but carries specific risks that must be actively mitigated:

Token theft: tokens stored in localStorage are accessible to JavaScript and can be stolen via XSS attacks. Store tokens in HttpOnly, Secure cookies which JavaScript cannot read.

Lack of revocation: a JWT is valid until its exp claim expires — there is no built-in way to invalidate a specific token mid-life. If a user logs out or their account is compromised, the token remains valid. Solutions: maintain a server-side token blacklist (reintroduces statefulness) or use very short expiration windows with refresh token rotation.

Token replay attacks: if a token is intercepted (e.g., MITM on HTTP), an attacker can reuse it. Enforce HTTPS everywhere to prevent interception. Bind tokens to a specific audience (aud claim) and origin where possible.

Algorithm confusion attacks: servers that accept both RS256 and HS256 can be tricked by an attacker who re-signs a tampered token using HS256 with the public key as the HMAC secret. Always specify the expected algorithm server-side — never trust the algorithm from the token header.

Long-lived tokens: tokens with multi-day or no expiry dramatically extend the blast radius of a breach. Use short-lived access tokens (~15 min) and rotate refresh tokens on each use.

Network & Infrastructure Security Questions

A firewall is a network security control that monitors and filters traffic based on predetermined rules at the network or transport layer. It makes allow/deny decisions based on IP addresses, ports, and protocols. Firewalls can be hardware appliances, software (iptables, Windows Firewall), or cloud-native (AWS Security Groups, GCP VPC Firewall Rules). Advanced next-generation firewalls (NGFW) operate at the application layer and can perform deep packet inspection.

A reverse proxy sits between external clients and backend servers, forwarding requests to the appropriate backend and returning responses to the client. The client never communicates directly with the backend server — the reverse proxy hides their identity and addresses. In addition to routing, reverse proxies provide: TLS termination, load balancing, caching, compression, rate limiting, and protection from DDoS by masking origin infrastructure. Examples: Nginx, HAProxy, Envoy, AWS ALB, Cloudflare.

Key distinction: a firewall enforces network access rules and blocks unwanted traffic at the connection level; a reverse proxy operates at the application level and provides both routing and additional security features. In production, they are complementary — firewalls protect the network perimeter and internal segmentation, while reverse proxies add application-layer controls.

Rate limiting controls the volume of incoming requests to a service within a time window. It protects backend services from several classes of abuse:

DDoS and volumetric attacks: by capping the request rate per IP or per client, rate limiting prevents a single source from exhausting server resources. Combined with traffic scrubbing at the CDN layer, it provides layered defense.

API abuse: without rate limits, a malicious or misconfigured client can send thousands of requests per second, starving legitimate users. Rate limiting enforces fair use and SLA compliance.

Credential stuffing and brute force: rate limiting login endpoints to a few attempts per minute makes automated password-guessing attacks economically infeasible.

Common algorithms: Fixed Window — simple counter per time window, but vulnerable to burst at window boundaries. Sliding Window — smooths out boundary bursts. Token Bucket — allows controlled bursting up to a bucket capacity, ideal for APIs with bursty legitimate traffic. Leaky Bucket — enforces a strictly constant output rate regardless of input burst.

Rate limits should be applied at multiple layers: CDN/edge for volumetric protection, API gateway for per-route quotas, and application code for business-logic-specific limits (e.g., per-user transaction frequency).

Zero Trust is a security model built on the principle of "Never Trust, Always Verify." Traditional perimeter-based security assumed everything inside the corporate network was trustworthy — once past the firewall, lateral movement was easy. Zero Trust eliminates this assumption: no user, device, or service is trusted by default, regardless of whether the request originates inside or outside the network.

Core principles: Verify every access request explicitly — authenticate and authorize using all available signals (identity, device health, location, service). Least privilege access — grant the minimum permissions required for the task, for the shortest necessary duration. Micro-segmentation — divide the network into small zones so that a compromised service cannot freely access other services. Continuous monitoring — log and analyze all traffic; flag behavioral anomalies in real time.

Importance for modern architectures: cloud workloads, remote workers, and microservices have dissolved the traditional perimeter entirely. Zero Trust is the only model that works when your "network" spans multiple clouds, on-premises data centers, and employee home networks. It also limits blast radius — even if one service or credential is compromised, tight segmentation and least-privilege policies prevent the attacker from moving laterally.

Kubernetes security is multi-layered — no single control is sufficient:

Container image security: use minimal, trusted base images (Alpine Linux, distroless). Scan images for CVEs before deployment using tools like Trivy or Clair. Sign images with Cosign to ensure only approved images are deployed.

RBAC: define fine-grained Kubernetes Role and ClusterRole objects. Service accounts should have the minimum permissions needed — avoid cluster-admin for application workloads. Audit RBAC policies regularly.

Pod Security Admission (PSA): enforce security profiles that prevent containers from running as root, disallow privilege escalation, restrict host path mounts, and enforce read-only root filesystems where possible.

Network Policies: by default, Kubernetes allows all pods to communicate with all other pods. Implement explicit NetworkPolicy resources to whitelist only the required pod-to-pod communication paths, blocking lateral movement.

Service mesh for mTLS: deploy Istio or Linkerd to automatically provision and rotate mTLS certificates for all service-to-service communication, with traffic policy enforcement at the sidecar proxy level.

Secrets management: never store secrets in plaintext in Kubernetes manifests or ConfigMaps. Use Kubernetes Secrets (encrypted at rest via KMS) or external solutions like HashiCorp Vault with the Vault Agent Injector or External Secrets Operator to pull secrets at runtime.

The OWASP Top 10 (2021) represents the most critical web application security risks:

Injection (SQL, NoSQL, OS command): attackers send malicious input that gets executed as code. Mitigate with parameterized queries, prepared statements, and WAF rules — never concatenate user input into queries.

Broken Authentication: weak passwords, missing MFA, credential stuffing. Mitigate with strong password policies, MFA, rate-limited login endpoints, and secure password hashing (bcrypt, Argon2).

Sensitive Data Exposure: unencrypted PII in transit or at rest. Mitigate with HTTPS/TLS everywhere, encryption at rest (AES-256), and data minimization.

XML External Entities (XXE): malicious XML input that reads files or SSRFs. Mitigate by disabling external entity processing in XML parsers and validating all XML input.

Broken Access Control: users accessing resources beyond their authorization. Mitigate with server-side authorization checks on every request, RBAC, and deny-by-default access policies.

Security Misconfiguration: default credentials, open cloud storage, verbose error messages. Mitigate with hardened baseline configurations, automated config auditing (CIS Benchmarks), and removing all unnecessary features and services.

Cross-Site Scripting (XSS): injected scripts run in victim's browser. Mitigate with output encoding, Content Security Policy (CSP) headers, and input sanitization.

Insecure Deserialization: untrusted data deserialized into objects, enabling RCE. Mitigate by avoiding deserialization of untrusted data, using signed/encrypted serialization formats.

Using Components with Known Vulnerabilities: outdated libraries with CVEs. Mitigate with automated dependency scanning (Snyk, Dependabot) and a patching process with SLA.

Insufficient Logging & Monitoring: attackers operate undetected for months. Mitigate with centralized, tamper-evident logging, real-time alerting on anomalies, and regular incident response drills.

A service mesh is a dedicated infrastructure layer — typically implemented via sidecar proxies (Envoy in Istio, Linkerd proxy) — that intercepts all service-to-service communication and applies policies transparently, without requiring application code changes.

Mutual TLS (mTLS): the mesh's built-in CA automatically provisions short-lived certificates for each service identity. Every connection between services is mutually authenticated and encrypted, eliminating the "trust the internal network" assumption. Certificate rotation is automated.

Access control policies: the mesh enforces which services are allowed to communicate with which other services. A service-to-service policy can be as specific as "Service A can call Service B's /payment endpoint using POST — nothing else."

Traffic observability: the sidecar proxies emit detailed telemetry — request rates, error rates, latency histograms — and full distributed traces. This makes anomalous communication patterns (unexpected service-to-service calls, spike in error rates) immediately visible.

Rate limiting and circuit breaking: the mesh can apply these resilience patterns at the infrastructure layer, protecting services from being overwhelmed without application-level code. Popular service meshes: Istio, Linkerd, Consul Connect, AWS App Mesh.

IAM (Identity and Access Management) is the control plane for cloud security — it governs who or what can access which cloud resources and what actions they can perform.

Key functions: Authentication — verifying the identity of users, applications, or services (via passwords, API keys, service account tokens, or federated SSO). Authorization — determining what an authenticated principal is permitted to do, expressed as IAM policies that attach to users, groups, roles, or resources. Least privilege enforcement — IAM policies allow fine-grained permission scoping: a Lambda function that reads from one S3 bucket should be granted exactly s3:GetObject on that specific bucket ARN, nothing broader. Audit logging — all IAM-governed API calls are logged (AWS CloudTrail, GCP Cloud Audit Logs) for compliance and incident investigation.

IAM also enables temporary credentials via role assumption (AWS STS AssumeRole) — workloads receive short-lived credentials that automatically expire, dramatically reducing the risk of long-lived credential leakage. In Kubernetes, IAM Roles for Service Accounts (IRSA) or Workload Identity (GCP) bind pod-level service accounts to cloud IAM roles, enabling fine-grained cloud API permissions without mounting static credentials into pods.

Further Reading

Quick Check

A user changes the URL from /api/invoices/1001 to /api/invoices/1002 and sees another customer's invoice. What failed?

Correct — this is an Insecure Direct Object Reference, a broken access control bug. The user authenticated fine; the API just never verified the invoice belonged to them. Authorize every object, every time.
Try It

Interactive: Rate Limiter Algorithms

Switch between Fixed Window, Sliding Window, and Token Bucket algorithms. Adjust the limit, then spam requests to see 429 responses appear. The sliding window is fairer than fixed — try sending 5 requests just before and just after a window boundary to see why.