Back to Security
Security
medium
mid

How do you secure a frontend application against common attacks?

Layered defense: HTTPS + HSTS, secure cookies (HttpOnly+Secure+SameSite), Content-Security-Policy (script-src nonces, no unsafe-inline), framework auto-escape + DOMPurify for HTML, CSRF tokens or origin check on state-changing requests, parameterized queries (SQL/NoSQL), input validation at boundaries (Zod), per-endpoint rate limit, secrets in env (never in code), dependency scanning (npm audit, Dependabot), least-privilege auth scopes, monitoring + alerting on anomalies. OWASP Top 10 is the canonical checklist.

12 min read·~5 min to think through

Security is layered — no single control is sufficient. The goal is defense-in-depth: each layer catches what others miss.

1. Transport

  • HTTPS everywhere — no HTTP fallback.
  • HSTS (Strict-Transport-Security: max-age=31536000; includeSubDomains; preload) — force HTTPS for future requests, even if user types http://.
  • Modern TLS (TLS 1.2+, prefer 1.3).
  • HSTS preload list for max effect.

2. Authentication

  • Bcrypt / Argon2 for password hashing (never MD5/SHA1/plain).
  • MFA (TOTP, WebAuthn, passkeys) for sensitive accounts.
  • OAuth / OIDC for federated login; PKCE for SPA flows.
  • Short-lived access tokens + refresh token rotation.
  • Rate limit login attempts (per IP and per account).
  • Account lockout after N failures, with notification.

3. Session / cookies

ts
Set-Cookie: session=abc; HttpOnly; Secure; SameSite=Lax; Max-Age=3600
  • HttpOnly: JS can't read.
  • Secure: HTTPS only.
  • SameSite=Lax: cross-site CSRF defense (browser default).
  • Short Max-Age + rotation.
  • Logout invalidates server-side (don't rely on cookie expiry alone).

4. CSRF

  • SameSite=Lax cookies (already strong default).
  • Plus CSRF token (double-submit cookie or synchronizer token) for state-changing endpoints.
  • Validate Origin / Sec-Fetch-Site headers.
  • Reject GET for mutations.

5. XSS

  • Framework auto-escape ({value} in React).
  • DOMPurify for unavoidable HTML.
  • No innerHTML / eval / new Function with user input.
  • URL scheme validation (block javascript:).
  • CSP as defense-in-depth:
ts
Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-abc' 'strict-dynamic';
  object-src 'none';
  base-uri 'none';
  • Trusted Types in Chromium for DOM XSS elimination.

6. Injection (SQL, NoSQL, command, LDAP)

  • Parameterized queries always. Never string-concatenate SQL.
  • ORM with prepared statements (Prisma, Drizzle, TypeORM).
  • For shell: never exec(userInput). Use library APIs.
  • For NoSQL: avoid user-provided operators ($where, $expr in MongoDB).

7. Authorization

  • Server-enforced, never client-controlled. The browser-visible UI hides admin features; the server also checks "is this user allowed?"
  • Least privilege: each role gets only what it needs.
  • Object-level: "can user X access record Y?" — check on every request.
  • Don't expose internal IDs that allow guessing other users' resources.

8. Input validation

  • At boundaries: Zod / Yup / Joi for request bodies, query params, form data.
  • Validate types AND values AND ranges.
  • Allowlist > blocklist.

9. Headers

ts
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: …
X-Content-Type-Options: nosniff
X-Frame-Options: DENY                ← or frame-ancestors in CSP
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()

10. Rate limiting + abuse

  • Per-endpoint per-user limit.
  • Per-IP limit (with CDN/WAF help).
  • Stricter limits on auth + signup.
  • Capcha (hCaptcha, reCAPTCHA) for high-risk flows.
  • Account creation throttling to limit sybil attacks.

11. Secrets

  • Environment variables or secret manager (AWS Secrets Manager, Vault).
  • Never in code or git.
  • Rotate periodically.
  • Per-environment (dev / staging / prod different).
  • Scan git history (truffleHog, GitGuardian) to catch accidental commits.

12. Dependencies

  • npm audit / Dependabot for known CVEs.
  • Pin versions (no ^ for security-critical deps).
  • Audit before adding new deps (bundlephobia + GitHub stars + maintenance signals).
  • SCA tools (Snyk, Socket.dev).
  • Lockfile committed.

13. File uploads

  • Validate file type by magic bytes (not just MIME).
  • Cap file size.
  • Scan for malware if user-facing.
  • Don't serve uploads from your origin — use a separate origin or CDN to limit XSS risk.
  • Store outside webroot; serve with explicit Content-Type + Content-Disposition.

14. Logging + monitoring

  • Log auth events (login, logout, password reset, MFA enroll).
  • Alert on anomalies (impossible travel, spike in 401s, password spraying).
  • Don't log secrets / passwords / tokens.
  • Centralize (Datadog, Splunk, ELK).
  • Sentry for application errors.

15. Pen testing + bug bounty

  • Periodic external pen test (annually for production apps).
  • Bug bounty program (HackerOne, Bugcrowd) once mature.
  • Internal red team for high-risk products.

16. SDLC

  • Code review on every PR.
  • SAST (Semgrep, CodeQL, ESLint security plugins).
  • DAST (ZAP, Burp) in staging.
  • Security training for engineers.
  • Threat modeling for new features.

OWASP Top 10

The canonical list of common web vulnerabilities. Current categories:

  1. Broken Access Control
  2. Cryptographic Failures
  3. Injection
  4. Insecure Design
  5. Security Misconfiguration
  6. Vulnerable and Outdated Components
  7. Identification and Authentication Failures
  8. Software and Data Integrity Failures
  9. Security Logging and Monitoring Failures
  10. Server-Side Request Forgery (SSRF)

Audit against this list as a baseline.

Mental model

Security is process + defense in depth. No single header / library / lint rule is enough. Layer transport, auth, sessions, input validation, output encoding, CSRF defense, CSP, secrets management, dependency scanning, rate limiting, monitoring. Make secure default and insecure require explicit opt-out.

Follow-up questions

  • What's the difference between authentication and authorization?
  • How does SameSite=Lax combine with CSRF tokens?
  • What should you scan with Dependabot / Snyk?
  • How do you do threat modeling?

Common mistakes

  • Tokens in localStorage — XSS exfiltrates.
  • String-concat SQL — injection.
  • Trusting client-side authorization checks.
  • Secrets in git history.
  • No rate limit on login — brute force.
  • MD5/SHA1 for password hashing.

Performance considerations

  • Most security headers have zero perf cost. Bcrypt/Argon2 have intentional CPU cost — tune work factor for ~100ms per hash. Rate limiting requires fast lookup (Redis). CSP/CSRF token checks are negligible.

Edge cases

  • SSRF via user-provided URLs — server fetches internal endpoints.
  • Open redirect — phishing vector.
  • Race conditions in critical operations (token use, payment processing).
  • Time-of-check vs time-of-use bugs.
  • Cache poisoning via Vary header misconfiguration.

Real-world examples

  • OWASP ASVS is a detailed standard for app security verification.
  • PortSwigger Web Security Academy is the canonical training.
  • Major breaches (Equifax, MyFitnessPal, etc.) usually trace to one or two missed controls — not novel attacks.

Senior engineer discussion

Seniors design security in layers, automate enforcement (lint, scanners, CI), monitor in production, and threat-model new features. They treat 'we use [framework]' as a half-truth — frameworks help with some attack surfaces but not authorization, secrets, dependencies, or input validation.

Related questions