All jobs / Full-stack developer

Security interview questions

Security questions a Full-stack developer can be asked, from first job to senior. Open one to see the strongest answer and why it works.

  1. Why do we serve every page over HTTPS and not just the login form?

    The strongest answer: Because it protects integrity as well as secrecy: on plain HTTP anyone on the path can read or alter the page and inject script, and the session cookie travels on every request, not only on login

    Why it works: Right — confidentiality, integrity and authenticity on every request. A page that can be rewritten in transit can steal the session on the next one.

    • HTTPS is integrity, not just secrecy.
    • Anything on HTTP can be rewritten.
    • Sessions travel on every request.
    • Redirect HTTP and set HSTS.
  2. You are choosing where the logged-in session lives on a first-party web app.

    Where do you put the session?

    The strongest answer: An HttpOnly, Secure cookie the JavaScript cannot read — XSS then cannot steal the session

    Why it works: Exactly. If script cannot read the cookie, a typical XSS cannot exfiltrate the session. Pair it with CSRF defence.

    • Prefer an HttpOnly Secure cookie for a first-party session.
    • localStorage is visible to XSS; treat that as the cost of a Bearer-in-JS design.
    • Cookies need CSRF protection; tokens in JS need a tight CSP and a short life.
    • Never put a session in the URL.
  3. A teammate notices a third-party API key in a committed .env file that has been on the default branch for two weeks.

    In what order do you deal with it?

    The strongest answer: Rotate the key first and assume it has been used, then remove it from the code and the history, move it into the secret store, and check the provider’s logs for activity you cannot account for

    Why it works: Exactly — rewriting history does not un-leak a secret. Rotation is the only step that actually invalidates it.

    • Rotate first; clean up second.
    • Assume a leaked secret was used.
    • Purge the history afterwards.
    • Move it into the secret store.
  4. The site is on HTTPS. Someone deploys the API on plain HTTP 'for a minute'.

    What do you do?

    The strongest answer: Serve the API on HTTPS too — the browser will block or warn on mixed content and cookies will not stay `Secure`

    Why it works: Exactly. A secure page cannot talk to an insecure API as if nothing happened. The whole stack is HTTPS or it is not.

    • HTTPS is end-to-end, not a homepage badge.
    • API, assets and cookies all need TLS.
    • Mixed content is a browser block, not a style warning.
    • `Secure` cookies require an HTTPS API.
  5. A teammate pastes a third-party API secret into the React app 'just for the demo'.

    What do you do?

    The strongest answer: Keep the secret on the server and have the UI call our API — anything in the bundle is public

    Why it works: Exactly. View Source is a user role. Secrets stay behind your API.

    • The browser is a hostile place for secrets.
    • Call your own API; let the server hold the third-party key.
    • Do not hide keys in minification or encoding.
    • If a stranger can view source, they have the value.
  6. You chose an HttpOnly session cookie instead of a token in localStorage.

    What else do you need?

    The strongest answer: CSRF defence — a SameSite cookie plus a token or custom header the other site cannot set — because the browser will send the cookie on its own

    Why it works: Exactly. Cookie sessions are automatic. That is the CSRF hole. SameSite helps; a token or custom header finishes the job.

    • Cookie auth implies CSRF work.
    • SameSite=Lax blocks many cross-site POSTs, not all cases.
    • Add a CSRF token or a header only your JS sets.
    • HTTPS is necessary and not sufficient.
  7. You are setting the session cookie on `app.example.com`.

    Which flags do you set?

    The strongest answer: `Secure`, `HttpOnly`, `SameSite=Lax` (or Strict), a tight `Path`, and no wide `Domain` unless a real subdomain needs it

    Why it works: Exactly. Each flag closes a door: HTTPS-only, no JS, less CSRF, smaller blast radius. Wide Domain is how a forgotten staging host steals the session.

    • Secure + HttpOnly + SameSite + tight Path.
    • Domain only for a real shared parent.
    • Do not ship debug cookies.
    • Defaults are not a threat model.
  8. A reviewer says the app is safe from XSS because the API strips script tags out of anything a user submits.

    Is that the right place for the defence, and where else does XSS get in?

    The strongest answer: Escaping belongs where the value is rendered; frameworks do that by default and the danger is where you bypass them — raw HTML, URLs and attributes — including data stored before the filter existed

    Why it works: Exactly — the same value is safe as text and dangerous in an attribute or a URL, so the context at render decides the encoding.

    • Encode at render, by context.
    • Do not bypass the framework.
    • Old and imported data is unfiltered.
    • Blocklists lose; encoding wins.
  9. The SPA keeps a JWT in localStorage and attaches it as Bearer.

    What do you recommend?

    The strongest answer: Move the session to an HttpOnly Secure SameSite cookie and treat XSS as a theft risk we refuse to make easy

    Why it works: Exactly. HttpOnly is the difference between XSS-as-annoyance and XSS-as-account-takeover. Then you add CSRF work.

    • HttpOnly cookie > JWT in localStorage for first-party apps.
    • XSS should not be able to `JSON.stringify` the session.
    • Then ship CSRF and a short session lifetime.
    • A readable cookie is the worst of both models.
  10. A request arrives with a JWT in the Authorization header. What does your API check before it trusts anything inside it?

    The strongest answer: The signature against the issuer’s key with the algorithm fixed by the server, then issuer, audience and expiry — and only then does it read the claims, still checking that this user may touch this record

    Why it works: Right — verify before you read. Pinning the algorithm closes the classic “alg: none” and key-confusion attacks, and authentication is still not authorisation.

    • Verify, then read.
    • Signature with a pinned algorithm.
    • Check issuer, audience and expiry.
    • Authorise the action separately.
  11. You set the session cookie to `SameSite=Lax` and a teammate wants to delete the CSRF token.

    Is SameSite=Lax enough?

    The strongest answer: It blocks many cross-site POSTs, not all CSRF — I still want a token or a custom header on cookie-authenticated writes

    Why it works: Exactly. Lax is a strong default, not a complete CSRF story (top-level GET, some browser quirks, older clients). Defence in layers.

    • SameSite=Lax plus a CSRF token or custom header.
    • Do not announce that CSRF is 'over'.
    • None+Secure is for true cross-site embeds, not a default.
    • Layer the browser rule and the application rule.
  12. A user asks you to erase their account. Postgres, Elasticsearch and the SPA all have their name.

    What does 'delete this user' include?

    The strongest answer: Primary store, search index, app caches, and any PII the UI kept in localStorage or the last-user screen — then confirm the UI cannot resurrect them

    Why it works: Exactly. Erasure is a path through the stack. A 'deleted' user who still greets you from localStorage is a complaint, not a complete job.

    • Map every place the person lives.
    • Delete or truly anonymise each, including the client.
    • The UI must not show a cached profile after.
    • Do not invent a retention period you cannot point to in policy.
  13. Cookie session, fetch POSTs, no traditional HTML form.

    How do you CSRF-protect a JSON SPA?

    The strongest answer: A cookie the JS can read (or a meta token) sent back as a header, plus SameSite — a third-party site cannot read that value to echo it

    Why it works: Exactly. Double-submit or a synchroniser token works because the attacker cannot read your cookie to set the header. Pair with SameSite and Origin checks.

    • Token in cookie or page, echoed on writes.
    • SameSite + Origin as extra layers.
    • Do not rely on Content-Type folklore alone.
    • The defence has to run on the API, not only in JSX.
  14. The site sits behind a load balancer that terminates TLS. Users hit a redirect loop, and password-reset emails link to http.

    What is happening, and what is the fix?

    The strongest answer: The app only ever sees plain HTTP from the balancer, so its redirect to HTTPS never ends and its generated links are wrong; honour X-Forwarded-Proto from the balancer only, and keep cookies Secure

    Why it works: Right — the proxy knows the scheme and the app does not; the forwarded header carries it, and it is only trustworthy from a known proxy.

    • The proxy knows the scheme.
    • Honour forwarded headers from it only.
    • Never trust them from the internet.
    • Secure cookies, correct absolute URLs.
  15. Support needs a user signed out of every device immediately after an account takeover, and the API uses stateless access tokens.

    What can you actually do?

    The strongest answer: Keep access tokens short-lived and revoke at the refresh step: delete the refresh token and bump a token version the API checks, accepting that an issued access token stays valid for its last few minutes

    Why it works: Exactly — statelessness is the trade: no lookup per request, bought by giving up instant revocation, so the window has to be short.

    • Short life, revoke on refresh.
    • A version claim checked at verification.
    • Denylists reintroduce per-request state.
    • Name the window you accept.
  16. A user wants a copy of their data and then wants it gone.

    What does a complete request look like?

    The strongest answer: An export of what you actually store, then erasure or anonymisation on the same map — including search, logs you can still key, and client-held PII — with a ticket the UI can show as done

    Why it works: Exactly. Access and erasure are product features with a map. Soft-delete that still serves is theatre. Do not invent a statutory deadline from memory.

    • Map, export, erase, prove it in the UI.
    • Soft-delete must be honoured by every read.
    • Include client storage and searchable logs.
    • No invented deadlines.
  17. A designer wants the staging build to show real names so screenshots look right.

    What must never ship in the bundle?

    The strongest answer: Maps of real users, emails, phone lists, or 'debug fixtures' copied from prod — the bundle is public, cached and diffable

    Why it works: Exactly. JS is a publication. Fixtures are either synthetic or they are a breach. 'Private' paths on a static host are still 200 OK.

    • Bundles are public documents.
    • Fixtures are synthetic.
    • Hashing emails does not make a list shippable.
    • No prod extracts in `/static`.
  18. A teammate says the API is safe from CSRF because the UI sends `application/json`.

    When is Content-Type not a CSRF defence?

    The strongest answer: When the API also accepts form-encoded or text bodies, or a framework parses JSON without the header — an attacker can still fire a simple request; do not treat `application/json` as a door lock

    Why it works: Exactly. Simple requests (form POST, no custom headers) skip preflight. If your API is generous in what it parses, CORS theatre will not save a cookie session.

    • Audit what the API will parse.
    • Reject form bodies on JSON routes.
    • Still use a token or custom header.
    • Do not worship Content-Type.
  19. A customer’s penetration test lands with thirty findings across the SPA, the API and the infrastructure, and the account manager wants everything closed by Friday.

    How do you work through it?

    The strongest answer: Reproduce each finding first, rank by what an attacker actually gains and how reachable it is, not by the report’s label, fix classes rather than instances, and agree dates for the rest in writing

    Why it works: Right — a report is evidence, not a plan. Exploitability and blast radius set the order, and a class fix stops the next instance.

    • Reproduce before you rank.
    • Rank by exploitability and impact.
    • Fix the class, not the instance.
    • Put the remaining dates in writing.
  20. A security review asks whether SameSite=Lax is your whole CSRF story.

    What does 'enough' look like on a cookie SPA?

    The strongest answer: SameSite, Origin/Referer fail-closed on writes, a CSRF token or custom header, and no `GET` that mutates — one layer will fail a browser you have not met

    Why it works: Exactly. Defence in layers is the 2026 answer because each control has a hole (Lax vs top-level GET, missing Origin on some clients, XSS reading a JS token).

    • Layer SameSite, Origin, and a token/header.
    • GET stays safe.
    • Do not claim one flag ended CSRF.
    • If you drop cookies for JWT, own the XSS cost.
  21. The SPA currently POSTs raw GraphQL documents from the browser.

    How do you ship GraphQL to production browsers?

    The strongest answer: Persisted queries (an allow-list of hashes) plus a cost limit — the CDN can cache GET-by-hash and a stranger cannot invent a warehouse-scanning query

    Why it works: Exactly. In production the browser should send an id, not a novel. That is cache, safety and a contract you can review like OpenAPI.

    • Allow-list queries in prod.
    • Hash or id on the wire.
    • Cost/depth limits anyway.
    • A JS 'secret' is not an ACL.
  22. You are moving the product onto the company identity provider, and the team asks whether the React app should hold the tokens.

    Which shape do you choose, and why?

    The strongest answer: Prefer a backend-for-frontend that completes the code flow and keeps the tokens server-side, giving the browser only a session cookie; authorization code with PKCE in the SPA is the fallback when you have no backend

    Why it works: Right — the strongest version keeps tokens out of a scriptable context entirely, and the cookie session then needs CSRF defence. Name that cost.

    • Keep tokens off the browser when you can.
    • BFF: cookie session, tokens server-side.
    • Public client: code flow plus PKCE.
    • No implicit flow, no client secret.
  23. A user requests erasure. Legal emails that the account is in a dispute hold.

    What if legal says hold?

    The strongest answer: Erasure yields to a documented hold: the UI must not show the person as an active user, the data stays in a restricted store, and you do not invent a 'we deleted it' email

    Why it works: Exactly. Staff-level GDPR is a conflict of duties, not a slogan. Hold is a legal state. The product hides them from normal use and does not lie about deletion.

    • States: active, held, erased.
    • Held data is locked, not 'kind of deleted'.
    • Normal UI treats them as gone.
    • Do not claim erasure you did not do.
  24. The SPA sends `X-Tenant-Id` on every call so the API can skip a lookup.

    Where does the tenant id come from?

    The strongest answer: From the session the server already trusts — never from a client header, query or JWT field the user can edit in DevTools

    Why it works: Exactly. Multi-tenant authz is a server fact. A header the browser sets is a header an attacker sets. Host-based tenancy is fine when you also bind it to the session.

    • Tenant comes from the session (or a signed host map).
    • Client headers are hints at most.
    • UI switch = server switch.
    • Log and deny cross-tenant like an incident.