Back to Cheat Sheets

🛡️ Leverage Browser Security Features

OWASP Top 10 Proactive Controls (2024) - C8

CORE CONTROL

📋 What Is This Control?

Leverage Browser Security Features is the proactive control of using the security mechanisms modern browsers already provide as a deliberate layer of defense. Every current browser ships an enforcement engine for response headers, cookie attributes, and HTML/HTTP directives that constrain what a page may do - which scripts run, which origins it talks to, whether it may be framed, how it is transported. This control is the discipline of turning those mechanisms on and configuring them correctly, so a class of client-side attacks is blocked even when a bug slips through your server-side code. It is a defense-in-depth layer, not a replacement for output encoding, validation, and access control.

C8 Proactive Control
Client-Side Focus Area
OWASP 2024 Proactive Controls

🎯 What It Prevents

  • XSS execution & session theft: a strict CSP blocks injected scripts; HttpOnly keeps session cookies out of JavaScript's reach
  • Data exfiltration: connect-src limits where a script can send stolen data
  • Clickjacking / UI-redress: frame-ancestors stops your page being framed
  • Protocol downgrade / SSL-strip / MITM: HSTS removes the plaintext window
  • CSRF: SameSite cookies withhold credentials from cross-site requests
  • Supply-chain script tampering: Subresource Integrity refuses a modified CDN script
  • MIME-sniffing to executable content via nosniff
  • Cross-origin data theft: allow-list CORS and COOP/COEP/CORP contain leaks

🧭 How It Works

1. Deny by default: start policies from 'none'/empty
and allow-list only what the app needs
↓
2. Set a strict, nonce-based CSP (strict-dynamic,
no unsafe-inline) - roll out report-only first
↓
3. Force HTTPS with HSTS; harden the session cookie
(HttpOnly + Secure + SameSite + __Host-)
↓
4. Add frame-ancestors, nosniff, Referrer-Policy,
Permissions-Policy, SRI, allow-list CORS
↓
5. Apply centrally on EVERY response; verify in CI

❌ Without This Control

# No policy declared - the browser enforces nothing # (no Content-Security-Policy header at all) // any inline or injected <script> executes → XSS runs freely # (no Strict-Transport-Security) // http:// is accepted → SSL-strip / downgrade on hostile networks Set-Cookie: session=abc123 ← no HttpOnly, Secure, or SameSite // readable by JavaScript (XSS steals it), sent on cross-site requests (CSRF) # (no X-Content-Type-Options) → MIME-sniffing to executable content # (no frame-ancestors) → page can be framed → clickjacking <script src="https://cdn.example.com/lib.js"></script> // no integrity hash → a tampered CDN script runs with full trust Access-Control-Allow-Origin: * ← reflected origin + credentials Access-Control-Allow-Credentials: true ← any site reads authed responses!

✅ Applying the Control

# Content-Security-Policy - per-response nonce, no unsafe-inline Content-Security-Policy: default-src 'self'; script-src 'nonce-{RANDOM}' 'strict-dynamic'; # only vetted scripts run object-src 'none'; base-uri 'none'; frame-ancestors 'none'; # anti-clickjacking connect-src 'self' https://api.example.com; # limits exfiltration require-trusted-types-for 'script'; upgrade-insecure-requests # HSTS - force HTTPS, serve only over HTTPS Strict-Transport-Security: max-age=63072000; includeSubDomains; preload # Session cookie - every protective flag + origin-bound __Host- prefix Set-Cookie: __Host-session=VALUE; HttpOnly; Secure; SameSite=Lax; Path=/ # Supporting headers, applied on every response X-Content-Type-Options: nosniff X-Frame-Options: DENY # legacy fallback Referrer-Policy: strict-origin-when-cross-origin Permissions-Policy: camera=(), microphone=(), geolocation=() <!-- Subresource Integrity - refuse a tampered CDN file --> <script src="https://cdn.example.com/lib.js" integrity="sha384-BASE64_HASH_OF_EXACT_FILE" crossorigin="anonymous"></script> # CORS is an allow-list, never '*' with credentials Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Credentials: true Vary: Origin

✓ Implementation Checklist

  • Content-Security-Policy set with nonces/hashes, not unsafe-inline
  • Strict-Transport-Security on HTTPS with long max-age + includeSubDomains
  • Session cookies carry HttpOnly, Secure, and appropriate SameSite
  • X-Content-Type-Options: nosniff on every response
  • Framing controlled with frame-ancestors (plus legacy X-Frame-Options)
  • Referrer-Policy set to avoid leaking URLs/tokens
  • Permissions-Policy disables browser features you do not use
  • Third-party script/link tags use Subresource Integrity
  • CORS is an explicit allow-list, never reflected origin with credentials
  • Headers applied centrally so they appear on errors and redirects too
  • CSP rolled out report-only first; violations reviewed before enforcing
  • Header regressions caught in CI on every deploy

🔍 Tools & Techniques

Configuration:

Helmet.js Flask-Talisman nginx / edge proxy CSP nonces Trusted Types DOMPurify

Verification:

SecurityHeaders.com SSL Labs testssl.sh CSP report-uri hstspreload.org

How to Verify:

  • Scan headers with a header-checking tool - fail the build on regressions
  • Confirm headers appear on error pages, redirects, and API responses
  • Roll out CSP in report-only and review violations before enforcing
  • Check the session cookie carries HttpOnly, Secure, SameSite, and __Host-
  • Verify CORS echoes only allow-listed origins, never a reflected wildcard
  • Confirm SRI hashes match the exact third-party files served

🌍 Why It Matters

These are recurring classes of client-side incident these features are designed to blunt - described as patterns, not specific vulnerabilities or vendors.

  • XSS leading to account takeover: an app reflects attacker input without adequate encoding and injected JavaScript reads the session cookie. HttpOnly puts the token out of reach and a strict nonce-based CSP stops the script executing at all - browser-enforced backstops behind the primary fix (output encoding).
  • Magecart-style third-party script tampering: attackers compromise a CDN-hosted payment or analytics script to skim form data. SRI refuses a script whose hash no longer matches, and a tight connect-src limits where stolen data can go.
  • Clickjacking / UI-redress: a malicious page frames a legitimate app and overlays deceptive UI. frame-ancestors 'none' prevents framing entirely.
  • SSL-strip on hostile networks & CSRF: HSTS refuses plaintext for the domain, and SameSite cookies stop credentials riding along on cross-site requests.

📌 Quick Tips

  • DO NOT treat a header as a substitute for fixing the underlying bug
  • DO NOT use unsafe-inline / unsafe-eval in CSP
  • DO NOT pair Access-Control-Allow-Origin: * with credentials
  • DO use a nonce/hash CSP with strict-dynamic
  • DO set HttpOnly + Secure + SameSite + __Host- on session cookies
  • DO apply headers centrally on every response
  • DO pin third-party code with Subresource Integrity