FSD Frontend System Design

Part II β€” The Shield Β· Lesson 3.4

Security Headers

In one lineSix response headers that close whole attack classes without touching application code.

Where you've seen itThe server config block that protects every route at once β€” including the legacy ones nobody has opened in two years.

  • Time17 min
  • DiagramHeader β†’ attack blocked table + CSP anatomy
  • Chapter3 Β· Security

Diagram

diagram
HEADER β†’ WHAT IT BLOCKS

  Content-Security-Policy      XSS execution, unexpected script origins
  Strict-Transport-Security    protocol downgrade, cookie sniffing
  X-Content-Type-Options       MIME sniffing (.txt executed as JS)
  Referrer-Policy              leaking URLs + tokens to third parties
  Permissions-Policy           camera/mic/geo abuse by you or your frames
  Cross-Origin-Opener-Policy   cross-window attacks, enables isolation

  Legacy but still seen:
  X-Frame-Options              clickjacking (superseded by frame-ancestors)
diagram
CSP ANATOMY β€” an allowlist for every resource type

  Content-Security-Policy:
    default-src 'self';                 ← fallback for everything
    script-src  'self' 'nonce-r4nd0m';  ← only your JS + nonced inline
    style-src   'self' fonts.googleapis.com;
    font-src    fonts.gstatic.com;
    img-src     'self' data: cdn.example.com;
    connect-src 'self' api.example.com sentry.io;
    frame-ancestors 'none';             ← nobody may frame you
    base-uri 'self';                    ← stops <base> hijacking
    object-src 'none';                  ← kills Flash/plugin vectors

  βœ— 'unsafe-inline'  β€” allows every injected <script>, defeats the point
  βœ— 'unsafe-eval'    β€” re-enables eval() and string setTimeout
diagram
ROLLOUT WITHOUT BREAKING PRODUCTION

  1. Content-Security-Policy-Report-Only + report-uri     nothing blocks
  2. watch violation reports for a week
  3. fix real gaps, allowlist genuine origins
  4. flip to Content-Security-Policy                      now enforcing

How it works

  1. Set headers at the edge β€” CDN, load balancer, or server config β€” so no route can miss them.
  2. Start CSP in report-only mode; a strict policy shipped blind takes the site down.
  3. Prefer nonces or hashes over 'unsafe-inline'; a nonce is generated per response and stamped on your own inline scripts.
  4. Strict-Transport-Security: max-age=31536000; includeSubDomains β€” then submit to the preload list once you're sure.
  5. Verify with the browser's Security panel or an external header scanner after every deploy.

Trade-offs

HeaderCost of getting it wrong
CSP too strictBroken third-party widgets, blank pages
CSP with 'unsafe-inline'Header present, protection absent
HSTS preloadHard to undo β€” you're committed to HTTPS
Referrer-Policy: no-referrerBreaks analytics attribution

Interview angle

"You ship a CSP and your payment widget stops loading. What now?"

  • Read the console violation: it names the directive and the blocked origin.
  • Add that specific origin to the specific directive β€” never widen to * or add 'unsafe-inline'.
  • Long term, run report-only in staging so violations surface before release, not after.

Recap

  • Headers are the highest-leverage security work: config, not code, applied everywhere.
  • A CSP containing 'unsafe-inline' is theatre.
  • Roll out report-only first, then enforce.