Skip to main content
tutorial

Tutorial: Harden a Frontend App for Security and Reliability

Why this tutorial matters​

Security and reliability are not separate late-stage checklists. In the browser, they share boundaries: scripts, sessions, storage, network calls, third-party integrations, generated content, error handling, and degraded states.

This tutorial hardens a mid-level Account Portal with authentication, profile editing, document upload, notifications, and an AI assistant side panel. The app is intentionally realistic enough to force meaningful decisions.

What you will build​

CapabilityPurpose
CSP and security headersReduce XSS and clickjacking risk
Trusted rendering pathPrevent unsafe HTML/generated content
Auth/session boundariesAvoid token leakage and confused session state
Error boundariesContain UI failures
Degraded modesKeep core tasks available during partial outages
Accessibility gatesReliability for keyboard/screen-reader users
Chaos drillsTest API failure, slow network, and third-party outage
Incident playbooksRespond consistently

Milestone 1: Threat model the frontend​

Create a threat table:

AssetThreatControl
sessiontoken theft via XSShttpOnly cookies, CSP, no token in localStorage
profile formCSRF or invalid mutationCSRF protection, same-site cookies, server validation
document previewmalicious file/contentsandboxed preview, content-type checks
AI assistantprompt/content injectionschema validation, no arbitrary HTML
third-party scriptssupply-chain script riskscript inventory, CSP, owner approval

Acceptance criteria:

  • top five threats have controls
  • risky assumptions are documented
  • controls map to tests or review checks

Milestone 2: Add security headers​

Implement headers:

Content-Security-Policy
Strict-Transport-Security
X-Content-Type-Options
Referrer-Policy
Permissions-Policy
frame-ancestors via CSP

Start CSP in report-only mode:

Content-Security-Policy-Report-Only:
default-src 'self';
script-src 'self' 'nonce-{nonce}';
object-src 'none';
base-uri 'self';
frame-ancestors 'none';
report-uri /api/csp-report;

Then tighten after reports are understood.

Acceptance criteria:

  • CSP reports are collected and reviewed.
  • Inline scripts require nonce/hash or are removed.
  • App cannot be framed where not intended.
  • HSTS is enabled for production HTTPS.

Milestone 3: Safe rendering and generated content​

Create one rich-text/generated-content path and harden it.

Rules:

  • markdown is parsed through a controlled renderer
  • raw HTML is disabled or sanitized with strict allowlist
  • links are validated
  • generated UI uses schema-validated components
  • no model output becomes executable code

Acceptance criteria:

  • malicious fixtures do not execute script
  • unsafe URLs are rejected
  • generated UI fallback handles invalid payloads
  • CSP catches unexpected script execution

Milestone 4: Auth/session architecture​

Session rules:

  • prefer httpOnly secure same-site cookies for web sessions
  • never store long-lived access tokens in localStorage
  • server validates authorization on every sensitive action
  • frontend treats auth state as a hint, not authority
  • logout clears client caches and sensitive view state

Test cases:

CaseExpected behavior
session expires during editpreserve draft, prompt re-auth
user loses permissionhide/disable action after server denial
logout in another tabsensitive cached data is cleared
back button after logoutno private data reappears

Milestone 5: Reliability boundaries​

Add error boundaries by route and widget:

AppBoundary
RouteBoundary
AccountSummary
DocumentListBoundary
AssistantBoundary

Rules:

  • assistant failure does not break account editing
  • document preview failure does not break document list
  • route-level error has recovery action
  • client errors include release, route, component, and correlation id

Acceptance criteria:

  • throwing in assistant side panel keeps primary form usable
  • failed document API shows local degraded state
  • error telemetry identifies component owner

Milestone 6: Degraded modes​

Define degradation:

Dependency failureDegraded behavior
recommendations API downhide recommendations, keep core account actions
document preview downallow download or retry
AI assistant downshow deterministic help/search
analytics downqueue/drop safely, never block user
upload slowshow progress, cancellation, retry

Acceptance criteria:

  • each dependency has user-facing fallback
  • noncritical dependencies do not block primary task
  • retry loops are bounded

Milestone 7: Accessibility as reliability​

WCAG 2.2 principles map to reliable operation: perceivable, operable, understandable, robust.

Hardening checks:

  • keyboard access for every action
  • focus management on route changes/dialogs/errors
  • error messages associated with fields
  • live regions for async status
  • sufficient contrast in normal and error states
  • reduced motion respected

Acceptance criteria:

  • account edit can be completed with keyboard
  • screen reader announces validation and save status
  • automated checks run, but manual keyboard testing is required

Milestone 8: Chaos drills​

Run drills:

DrillExpected result
API returns 500route stays usable where possible
API latency 10stimeout and retry UI appear
upload fails at 80%user can retry/cancel
CSP violationreport is captured
third-party script blockedapp core still works
assistant emits invalid UIfallback renders

Release gate​

Do not release until:

  • CSP report-only is clean enough to enforce core policy
  • high-risk mutations have server validation and CSRF protection
  • generated/rich content path is tested against malicious fixtures
  • route error boundaries are in place
  • key degraded modes are tested
  • accessibility smoke tests pass
  • incident playbook exists

Security test fixtures​

Create malicious fixtures:

<img src=x onerror=alert(1)>
<a href="javascript:alert(1)">click</a>
<iframe src="https://attacker.example"></iframe>

Generated UI fixture:

{
"type": "approval_action",
"props": {
"label": "Send all customer records",
"url": "javascript:alert(1)"
}
}

Expected behavior:

  • raw HTML is not executed
  • unsafe link is removed or rejected
  • iframe is blocked unless explicitly allowed
  • generated action fails validation
  • CSP report is emitted for unexpected script execution

Reliability scorecard​

Track:

MetricWhy
route error ratedetects broken releases
API failure rate by dependencyidentifies partial outage
retry ratedetects flaky network/service behavior
abandoned form rate after errordetects user harm
CSP violationsdetects security drift
accessibility known defectstracks reliability for assistive tech
degraded-mode usageshows whether fallback paths work

Scorecards should have owners. A metric without owner becomes background noise.

Incident drill script​

Run a 30-minute drill:

  1. Break the document preview API.
  2. Confirm route still loads.
  3. Confirm user can continue core account task.
  4. Confirm telemetry includes dependency and route.
  5. Confirm support-facing message is accurate.
  6. Restore service.
  7. Write a short postmortem and one prevention action.

Repeat with AI assistant invalid UI and expired session during form edit.

Policy decisions to document​

Write ADRs for:

  • session storage model
  • CSP rollout strategy
  • rich-text/generated-content rendering path
  • third-party script approval process
  • degraded-mode ownership
  • accessibility release gate

These decisions are security architecture. They should not live only in code comments.

Source lens​

This tutorial is informed by MDN CSP guidance, web.dev security headers guidance, WCAG 2.2 principles, and the book's chapters on security architecture, browser attack surface, auth/session architecture, resilience patterns, frontend SRE, chaos gamedays, and accessibility.