Security for Legal SaaS

Episode 12 · Module 3 · App Security

Browser Security Headers

18 May 2026 · 8:57 · Security for Legal SaaS

0:00 8:57

Your browser will execute JavaScript from any origin, embed your pages in iframes, and send credentials to any domain — unless you tell it not to. Alice and Dan walk through CORS, CSRF, Content Security Policy, X-Frame-Options, and HSTS — the headers that turn your users’ browsers from liability into defence.

Today’s Lesson

Security for Legal SaaS — Episode 12: Browser Security Headers

The Browser as Battleground

Your legal SaaS application runs in your users’ browsers. Those browsers will happily execute JavaScript from any origin, embed your pages in iframes, and send credentials to any domain — unless you explicitly tell them not to. Browser security headers are instructions from your server to the browser, declaring what is and isn’t permitted. Get them wrong, and you’ve handed attackers a proxy inside your user’s authenticated session.

Key stat: The 2023 Verizon DBIR found that web application attacks constituted 26% of all breaches, with credential theft and session hijacking as primary objectives. Properly configured security headers eliminate entire classes of these attacks.

For legal tech — where a single authenticated session may access privileged case files, billing data, and client communications — browser-level protections are not optional polish. They are the last line of defence when everything else fails.

CORS: Who Gets to Read Your Responses

Cross-Origin Resource Sharing (CORS) controls which external origins can make requests to your API and read the responses. The Same-Origin Policy blocks cross-origin reads by default — CORS relaxes it selectively.

The Misconfiguration That Opens Everything

Configuration Risk Level Effect
Access-Control-Allow-Origin: * Critical (with credentials) Any website can read your API responses
Reflecting the Origin header back Critical Attacker’s domain gets access automatically
Access-Control-Allow-Credentials: true + wildcard Critical Explicitly forbidden by spec but some servers try
Specific trusted origins only Correct Only your own domains can read responses

Real-world example: PortSwigger research demonstrated that CORS misconfigurations in production applications allowed attackers to steal user data cross-origin by simply reflecting the attacker's Origin header in the Access-Control-Allow-Origin response.

For legal SaaS: If your document API reflects any Origin header and includes Access-Control-Allow-Credentials: true, an attacker’s website can silently read your user’s privileged case documents while they have an active session.

Correct Implementation

Maintain a strict allowlist of permitted origins. Validate the incoming Origin header against this list. Only echo back origins that match exactly. Never use regex patterns that can be bypassed (e.g., *.lawfirm.com matching evil-lawfirm.com).

CSRF: Forged Actions in Your User’s Name

Cross-Site Request Forgery tricks a user’s browser into making state-changing requests to your application using their existing session cookies. The browser automatically attaches cookies to same-site requests — the attacker doesn’t need to steal credentials.

Attack Scenario in Legal Tech

A lawyer visits a malicious page (perhaps linked in an email from opposing counsel). The page contains a hidden form that submits to your application’s API:

- POST /api/matters/{id}/share — shares a privileged case with an external email

- POST /api/documents/{id}/access — grants document access to a new user

- POST /api/billing/transfer — initiates a trust account transfer

Defence Layers

Defence How It Works
CSRF tokens Server generates a random token per session; form submissions must include it. Attacker’s page cannot read it (Same-Origin Policy).
SameSite cookies SameSite=Lax (default in modern browsers) blocks cookies on cross-site POST requests. Strict blocks them on all cross-site requests.
Origin/Referer validation Verify the Origin or Referer header on state-changing requests matches your domain.
Custom request headers Require a custom header (e.g., X-Requested-With) that simple cross-origin forms cannot set.

The SameSite cookie attribute has dramatically improved the baseline — Chrome, Firefox, and Edge all default to Lax. But explicit CSRF tokens remain necessary for older browser support and defence-in-depth.

How CSRF Actually Works

The attack exploits two facts about how browsers handle cookies. First, when you log into a site, your browser stores a session cookie and automatically attaches it to every subsequent request to that domain — you don’t re-authenticate on each click. Second, the target server blindly trusts any request bearing a valid cookie, assuming you intentionally initiated it.

Here’s the sequence: you log into bank.com and your session cookie is active. In a separate tab, you visit a malicious page. That page contains a hidden HTML form pointing at bank.com/transfer with pre-filled values (recipient: attacker, amount: $1,000). A tiny script auto-submits the form the instant the page loads. Your browser fires the POST request to bank.com — and because the destination is bank.com, it automatically glues your login cookie onto the request. The bank receives an authentic-looking request with your valid session, processes the transfer, and you never saw a thing.

The attacker’s payload is trivially simple — a hidden form with a JavaScript one-liner to submit it:

html
<form id="csrfForm" action="https://bank.com/transfer" method="POST">
    <input type="hidden" name="toAccount" value="hacker_123" />
    <input type="hidden" name="amount" value="1000" />
</form>
<script>document.getElementById('csrfForm').submit();</script>

A common misconception is that browser tab isolation should prevent this. The browser’s Same-Origin Policy does block a malicious site from reading data from another tab. But it does not stop it from sending data. CSRF only needs to send a blind command — it doesn’t care about the response. That’s the loophole.

The two primary defences close this gap. Anti-CSRF tokens embed a server-generated random value in legitimate forms; the attacker cannot read it cross-origin, so forged submissions fail validation. SameSite cookie attributes (Lax or Strict) instruct the browser to strip cookies from cross-site requests entirely, removing the automatic attachment that makes the attack possible.

Content Security Policy (CSP)

CSP tells the browser exactly which sources of content are permitted — scripts, styles, images, fonts, frames, and more. A strict CSP is the most effective defence against Cross-Site Scripting (XSS).

CSP for Legal SaaS

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-{random}';
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https://cdn.yourdomain.com;
  frame-ancestors 'none';
  form-action 'self';
  base-uri 'self';
Directive Purpose
script-src 'self' 'nonce-{random}' Only execute scripts from your domain or with a per-request nonce
frame-ancestors 'none' Prevent your app from being embedded in iframes (replaces X-Frame-Options)
form-action 'self' Forms can only submit to your own origin
base-uri 'self' Prevents <base> tag injection that redirects relative URLs

Deployment tip: Start with Content-Security-Policy-Report-Only to collect violation reports without breaking functionality. Report URI or your own endpoint receives JSON reports of what would have been blocked. Tighten over weeks, then enforce.

X-Frame-Options and Clickjacking

X-Frame-Options prevents your application from being embedded in an iframe on an attacker’s page. Without it, an attacker overlays transparent iframes of your app over a decoy page — the user thinks they’re clicking a harmless button but actually clicking “Share Document” or “Approve Transfer” in your application.

For legal SaaS: X-Frame-Options: DENY (or use the equivalent frame-ancestors 'none' in CSP). There is almost never a legitimate reason to allow external framing of a legal application.

HSTS: Forcing HTTPS

HTTP Strict Transport Security (HSTS) tells browsers to only connect to your domain over HTTPS — never HTTP. This prevents SSL stripping attacks where a man-in-the-middle downgrades the connection.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Submitting to the HSTS preload list hardcodes your domain into browser source code — HTTPS is enforced even on the first visit, before any header is received.

Headers as Defence-in-Depth

No single header stops all attacks. The security model is layered:

Attack Primary Defence Header Backup
XSS Input sanitisation + output encoding CSP blocks inline script execution
Clickjacking Frame-busting JS X-Frame-Options / CSP frame-ancestors
MITM downgrade TLS configuration HSTS forces HTTPS
Session hijacking Secure cookie flags SameSite + CSP prevents exfiltration vectors
Data theft via embedding Authentication CORS blocks cross-origin reads

Automated Scanning

Don’t rely on manual header checks. Mozilla Observatory grades your site’s security headers and provides specific remediation guidance. SecurityHeaders.com by Scott Helme offers instant analysis with explanations for each missing or misconfigured header.

Run these scans in CI/CD — a deployment that removes a security header should fail the pipeline, not reach production.

Conclusion

Browser security headers are cheap to implement and expensive to forget. A single misconfigured CORS policy can expose every privileged document your users access. A missing CSP allows injected scripts to exfiltrate session tokens. For legal SaaS, where the browser session is the gateway to attorney-client privilege, these headers are not optimisation — they are infrastructure.

Next episode: TLS and HTTPS from Scratch — certificates, cipher suites, and why “just add HTTPS” involves more decisions than you’d expect.

Sources & references

  1. OWASP, "Secure Headers Project." Comprehensive reference for HTTP security headers
  2. Verizon, 2023 Data Breach Investigations Report. Web application attacks in 26% of breaches
  3. MDN Web Docs, "Same-origin policy." Foundation of browser security model
  4. WHATWG, "Fetch Standard — CORS protocol and credentials." Spec-level CORS credential rules
  5. James Kettle, "Exploiting CORS misconfigurations for Bitcoins and bounties," PortSwigger Research
  6. OWASP, "Cross-Site Request Forgery Prevention Cheat Sheet."
  7. MDN Web Docs, "SameSite cookies." Browser cookie isolation by site context
  8. MDN Web Docs, "Content Security Policy (CSP)."
  9. Report URI, "CSP Reporting." Hosted CSP violation report collection
  10. MDN Web Docs, "X-Frame-Options." Clickjacking prevention header
  11. OWASP, "HTTP Strict Transport Security Cheat Sheet."
  12. HSTS Preload List Submission. Hardcode HTTPS enforcement into browsers
  13. Mozilla Observatory. Automated security header grading
  14. Scott Helme, SecurityHeaders.com. Instant header analysis tool