Fundamentals

Security

Authentication vs. authorization, sessions vs. JWTs, SSRF, secrets management, and least privilege.

13 minMediumsecurityfundamentals

Security rarely gets its own deep dive in a 45-minute interview, but it shows up constantly as a two-minute question tacked onto the end of another one — "how would you authenticate this," "what stops someone from abusing that endpoint." Having crisp, short answers ready is worth more here than anywhere else in this track.

Authentication vs. authorization

Two different questions, often conflated. Authentication answers "who is this" — logging in, verifying a token. Authorization answers "what is this identity allowed to do" — a logged-in user might still be forbidden from deleting someone else's post. A system that checks authentication carefully but skips authorization on individual resources is a common, real vulnerability class (an authenticated user hitting DELETE /posts/:id for a post that isn't theirs) — naming that these are separate checks, not one, is worth doing explicitly rather than assuming it's obvious.

Sessions vs. tokens

Session (cookie + server-side store)JWT (self-contained token)
RevocationImmediate — delete the server-side rowHard — the token is valid until it expires, unless you maintain a blocklist (which reintroduces server-side state anyway)
Server stateRequires a shared store (e.g. Redis) if there's more than one serverNone needed to verify the token — any server with the signing key can check it
Good fitMost first-party web appsService-to-service calls, or a case where avoiding a shared session store genuinely matters

The trade-off in one line: a session is easy to revoke and needs shared state; a JWT needs no shared state to verify but is hard to revoke early. Short JWT expiries (issue a new one every few minutes, backed by a longer-lived refresh token) is the common way to get most of both — bounded exposure if a token leaks, without a session store on the hot path.

Transport security

TLS terminates the "is this connection actually private and unmodified" question at the network layer — assume it everywhere and don't reinvent it at the application layer. The detail worth having ready: TLS should be terminated as close to the actual backend as reasonable, not just at the public edge — internal service-to-service traffic inside a private network is still worth encrypting if the blast radius of one compromised internal host matters, which in most real production environments it does.

Cross-site scripting (XSS) and cross-site request forgery (CSRF)

Two different attacks that get confused for each other. XSS happens when attacker-controlled input gets rendered as executable script in another user's browser — a comment field that stores <script> verbatim and a page that renders it unescaped lets that script run with the victim's session. The fix is escaping output by default (nearly every modern frontend framework does this automatically) and never trusting raw-HTML rendering with unsanitized input. CSRF is different: it doesn't inject anything, it makes the victim's browser submit a request the victim never intended, using credentials the browser attaches automatically — a malicious page with a hidden auto-submitting form works because the browser sends the victim's session cookie regardless of which site the request originated from. The standard fix is a CSRF token — a value the legitimate page embeds and the server checks, that an attacker's page has no way to know — plus setting cookies SameSite=Lax or Strict, which stops the browser from attaching them to cross-site requests at all.

Injection

SQL injection is the classic version — building a query by concatenating raw user input lets an input like ' OR '1'='1 change the query's meaning entirely. Parameterized queries (or an ORM that generates them) close this off by sending the input as data, never as part of the query text. The same class of bug exists for NoSQL: passing an unsanitized JSON body straight into a MongoDB query lets an operator like {"$gt": ""} bypass an equality check the developer assumed was a plain string comparison.

Rate limiting and DoS as a security concern, not just a fairness one

The Rate Limiter case study frames rate limiting as fairness between clients — it's also a security control: an unauthenticated login endpoint with no rate limit is a standing invitation to brute-force credential guessing. Interviewers sometimes probe this directly: "what stops someone from trying every password against this endpoint" is a rate-limiting question wearing a security hat.

SSRF: when the server fetches a URL on someone else's behalf

Any design where the server fetches a URL supplied by a client — the Web Crawler case study is a design built entirely around doing exactly this — has to consider server-side request forgery: a malicious input pointing the server at an internal-only address (http://169.254.169.254/, a common cloud metadata endpoint, or an internal admin service with no auth because it "isn't public") instead of a real external page. The standard mitigation is an allowlist or explicit block on private/internal IP ranges before the server ever makes the outbound request, not just validating that the input "looks like a URL."

Secrets management

An API key or database credential belongs in a secrets manager (or, at minimum, an environment variable injected at deploy time) — never committed to source, and never returned in a response or log line. The interview-relevant version of this: if a design's API returns any kind of internal token or credential to a client, that's worth flagging as a design smell even if the interviewer didn't ask about it directly.

Encryption at rest

TLS protects data in transit; it says nothing about data sitting in a database or a backup. Encryption at rest means the data on disk is unreadable without the decryption key even if the disk itself is compromised or a backup is exfiltrated — most managed databases (RDS, DynamoDB) offer this as a checkbox rather than something to build, so the interview-relevant version is knowing to turn it on for anything sensitive (payment details, personal data) and naming that the encryption key itself needs its own access control, separate from the data it protects.

Least privilege and defense in depth

A service should hold only the permissions it actually needs — a notification worker that only sends emails shouldn't also have write access to the primary user database, even if it's convenient to grant broadly upfront. Defense in depth is the same idea at the system level: don't rely on a single control (the login page's rate limiter, say) as the only thing standing between an attacker and a bad outcome — authorization checks at the resource level, network-level segmentation, and input validation should each independently reduce risk, so that one control failing doesn't mean the whole system fails with it.