Subresource Integrity, HSTS, Security Headers
TL;DR
The “set these headers and you’ve solved 80% of low-hanging frontend security issues” set. HSTS forces HTTPS; SRI verifies third-party script integrity; X-Frame-Options / frame-ancestors block clickjacking; Referrer-Policy controls what URL goes out; Permissions-Policy locks down browser APIs. Use securityheaders.com or observatory.mozilla.org to check your site’s score in 60 seconds.
Interview Q&A
Q: Strict-Transport-Security (HSTS) — what it does.
A: Tells the browser “this site is HTTPS only — for the next N seconds, don’t even try HTTP.”
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Effect:
- For one year (
31536000seconds), browser refuses HTTP requests to this domain — even if user typeshttp://, browser silently upgrades. includeSubDomains— applies to all subdomains.preload— opts into the HSTS preload list (browser ships knowing your domain is HTTPS-only, no first-request HTTP vulnerability).
Without HSTS: a man-in-the-middle attacker can intercept the first HTTP request, prevent the redirect to HTTPS, serve attacker content. With HSTS: browser refuses HTTP, attacker can’t downgrade.
Always set on production. preload requires meeting criteria + submission to hstspreload.org.
Q: Subresource Integrity (SRI).
A: Verifies that a third-party script/stylesheet hasn’t been tampered with:
<script src="https://cdn.example.com/lib.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC"
crossorigin="anonymous"></script>
The browser:
- Fetches the script.
- Computes its SHA-384 hash.
- Compares to the
integrityvalue. - If mismatch — refuses to execute.
Use for any third-party script you load from a CDN. Without SRI, a CDN compromise (or attacker swapping the file) injects code into your origin.
crossorigin="anonymous" is required for SRI on cross-origin requests.
Generate hashes: openssl dgst -sha384 -binary lib.js | openssl base64 -A or via webpack-subresource-integrity plugin / Vite plugins.
Q: X-Frame-Options and frame-ancestors — clickjacking.
A: Both control who can frame your page (anti-clickjacking).
X-Frame-Options: DENY # legacy, single value
# or
Content-Security-Policy: frame-ancestors 'none' # modern, more granular
frame-ancestors is part of CSP, more flexible: frame-ancestors 'self' https://trusted-partner.com.
If both are set, modern browsers prefer frame-ancestors. Set both for max compatibility.
Without either: an attacker iframes your page, overlays misleading buttons, tricks the user into clicking your real button thinking it’s something else (clickjacking). See 08_clickjacking_and_supply_chain.md.
Q: Referrer-Policy — what URL leaks.
A: Controls what the Referer header contains when the user navigates away or loads a subresource.
Referrer-Policy: strict-origin-when-cross-origin # modern default
Common values:
| Value | What |
|---|---|
no-referrer |
never send Referer |
same-origin |
send full URL on same-origin, nothing cross |
origin |
send only origin (https://example.com) |
strict-origin |
origin only, but only over HTTPS |
origin-when-cross-origin |
full URL same-origin, origin only cross-origin |
strict-origin-when-cross-origin (default in Chrome 85+) |
like above, but downgrade safe |
unsafe-url |
always send full URL |
Why care: full URL (/users/12345/private-doc?token=...) leaking to third-party assets exposes sensitive data. The modern default is sane; don’t change unless you specifically need different behavior.
Q: Permissions-Policy (formerly Feature-Policy).
A: Disables browser APIs you don’t use, reducing attack surface.
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Empty parens = “no origins may use this API.” Even if XSS gets in, it can’t trigger getUserMedia because the policy denies.
Common deny list for typical apps: camera, microphone, geolocation, payment, usb, serial, bluetooth. Keep what you actually use.
Q: X-Content-Type-Options: nosniff.
A: Disables MIME-type sniffing — browser respects the Content-Type you sent.
X-Content-Type-Options: nosniff
Without it, browsers may “guess” content type and treat a text/plain response with JS-looking content as a script (historical IE issue, partly modernized). With nosniff, this can’t happen.
Always set. Trivial, removes a class of attack.
Q: Cross-Origin-Opener-Policy (COOP) and Cross-Origin-Embedder-Policy (COEP).
A: Newer isolation headers, mostly relevant for Spectre defense + enabling SharedArrayBuffer:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
- COOP: prevents
window.open’d page from interacting with the opener (and vice versa) across origins. - COEP: subresources must opt in via
Cross-Origin-Resource-Policyheader. Required for sites using shared memory or high-resolution timers.
For most apps: set COOP at minimum (same-origin). COEP only if you need SharedArrayBuffer (multithreaded WASM, advanced workers).
Q: Cross-Origin-Resource-Policy (CORP).
A: Server declaration: who can embed me as a subresource.
Cross-Origin-Resource-Policy: same-origin
Prevents cross-origin pages from loading this resource via <img>/<script>/etc. Useful for preventing unauthorized embeds of your assets.
Q: A senior’s baseline header set.
A:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-to csp-endpoint
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()
Cross-Origin-Opener-Policy: same-origin
X-Frame-Options: DENY # backward-compat for frame-ancestors
Set at the edge (CDN, reverse proxy, framework middleware) so they apply uniformly. Frameworks (Next.js, Nuxt) have config to emit these.
// next.config.js
const securityHeaders = [
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
{ key: "X-Content-Type-Options", value: "nosniff" },
// ...
];
module.exports = {
async headers() {
return [{ source: "/(.*)", headers: securityHeaders }];
},
};
Q: How do you audit?
A: Tools:
- securityheaders.com — scan, grade A+ to F, explain each missing header.
- Mozilla Observatory (observatory.mozilla.org) — more comprehensive.
- OWASP ZAP — automated security testing.
- Lighthouse — has a “Best practices” audit that catches a subset.
- CSP Evaluator (csp-evaluator.withgoogle.com) — analyzes your CSP for weaknesses.
Aim for A+ on securityheaders.com. Quick wins for most projects.
Gotchas / edge cases
- HSTS + dev environments — local HTTPS dev with self-signed cert can cause
localhostHSTS to stick. Usechrome://net-internals/#hststo clear. - SRI breaks on minor library updates — every release has a different hash. Pin a version + hash; don’t auto-bump.
- SRI + CORS — the CDN must serve with CORS headers; otherwise SRI check fails.
Referrer-Policy: no-referrerbreaks analytics that depend on referrer. Test.Permissions-Policysyntax changed fromFeature-Policy— semicolons vs commas, etc. Use a generator.- COOP + analytics popups — third-party popups can’t talk to opener; may break OAuth or share dialogs. Use
same-origin-allow-popups. - Headers vs
<meta>— some headers (X-Frame-Options, HSTS) only work as headers, not meta tags. Always prefer headers.
What a senior is expected to say
- “HSTS forces HTTPS-only; preload makes it permanent in browser distribution. SRI verifies third-party scripts haven’t been tampered with — essential for CDN-loaded libraries.”
- “CSP
frame-ancestors 'none'is the modern anti-clickjacking;X-Frame-Options: DENYis the legacy. Set both for compat.” - “
Referrer-Policy: strict-origin-when-cross-originis the sane default — prevents leaking full URLs cross-origin.” - “
Permissions-Policydenies browser APIs you don’t use — reduces attack surface even if XSS gets in.” - “Baseline header set: HSTS, CSP, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, COOP. Set at the edge, audit with securityheaders.com (target A+).”
- “SRI requires
crossorigin='anonymous'for cross-origin; the CDN must serve CORS headers.”
Cross-references
- CSP details: 03_csp.md
- Clickjacking deeper: 08_clickjacking_and_supply_chain.md
- CORS details: ../18_browser_internals/09_cors_deep.md
Further reading
- MDN — HTTP Security Headers: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
- securityheaders.com (scan + grade): https://securityheaders.com/
- OWASP Secure Headers Project: https://owasp.org/www-project-secure-headers/
- web.dev — Security headers: https://web.dev/articles/security-headers
- SRI Hash Generator: https://www.srihash.org/