RoadmapDay 52 / 80
System DesignMonth 3 · Week 11

Day 52: Web & Mobile Security: OWASP Top 10, XSS, CSRF, & SSL Pinning

Recognize and defend against the vulnerability classes most likely to show up in both a real security review and an interview.

Mark this day complete

Study

Concepts

XSS: attacker-controlled script execution in your page's context

Cross-Site Scripting happens when untrusted input is rendered as executable HTML/JS in your page. React's default text rendering (`{userInput}`) auto-escapes content, which is why React apps are relatively resistant BY DEFAULT — the real danger is `dangerouslySetInnerHTML` with unsanitized input, or building raw HTML strings on a server that get injected client-side. If you must render user-provided HTML (a rich-text comment), sanitize it through a library like DOMPurify immediately before rendering, never trust it as-is.

The consequence of a successful XSS is severe specifically because it runs WITH the user's full page privileges — it can read localStorage (including any tokens stored there, tying directly back to Day 51's storage guidance), make authenticated requests as the user, or exfiltrate form input in real time.

CSRF: tricking the browser into making a request it trusts

Cross-Site Request Forgery exploits the fact that browsers automatically attach cookies (including auth cookies) to ANY request to a domain, even one triggered by a malicious page the user happens to have open in another tab — a hidden auto-submitting form on `evil.com` can POST to `yourbank.com/transfer` and the browser will happily attach the user's valid session cookie. Defenses: `SameSite=Lax` or `Strict` cookies (the browser withholds the cookie on cross-site requests by default in modern browsers), CSRF tokens (a per-session random value the server checks on state-changing requests, unreadable/unforgeable by a third-party page), and requiring custom headers for state-changing requests (simple cross-site forms cannot set custom headers).

Note the asymmetry with token-based (Authorization header, non-cookie) auth: if your access token lives in memory and is sent via an `Authorization: Bearer` header rather than a cookie, CSRF does not apply to that request AT ALL, because a third-party page cannot set custom headers on a cross-origin form submission — this is one argument in favor of header-based auth for the access token even while using a cookie for the refresh token.

Mobile-specific: SSL/Certificate Pinning

Even over HTTPS, a device with a maliciously (or corporately) installed root certificate can man-in-the-middle traffic by presenting a certificate that the device trusts but that was not issued by the real server's CA. SSL Pinning hard-codes (pins) the expected certificate or public key inside the app itself, so the app rejects a connection even if the OS-level certificate store would otherwise trust the intercepting certificate — a meaningful extra layer for apps handling sensitive data (banking, health), at the operational cost of needing an app update if the pinned certificate/key ever needs to rotate.

See It

Visualizations

Visualization

XSS vs CSRF

 XSSCSRF
What runsAttacker JS, inside YOUR pageA legitimate request, from a DIFFERENT page
Root causeUnsanitized input rendered as HTML/JSBrowser auto-attaches cookies to any request
Primary defenseEscape by default, sanitize any raw HTML (DOMPurify)SameSite cookies, CSRF tokens, custom-header requirement
React's default postureSafer by default (auto-escaping)Depends entirely on cookie config, not framework-specific

Build It

Code Examples

Sanitizing user-provided HTML before rendering

jsx
import DOMPurify from 'dompurify';

function CommentBody({ rawHtml }) {
  // NEVER: dangerouslySetInnerHTML={{ __html: rawHtml }} directly
  const clean = DOMPurify.sanitize(rawHtml, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
    ALLOWED_ATTR: ['href'],
  });
  return <div dangerouslySetInnerHTML={{ __html: clean }} />;
}

CSRF-hardened cookie config + custom header requirement

js
// Server: set the refresh-token cookie defensively
res.cookie('refresh_token', token, {
  httpOnly: true,   // JS cannot read it (mitigates XSS exfiltration of this cookie)
  secure: true,     // HTTPS only
  sameSite: 'strict', // never sent on cross-site requests at all
});

// Also require a custom header on state-changing requests — a
// cross-site <form> submission cannot set custom headers, so this
// alone blocks the classic CSRF attack even without SameSite.
app.post('/api/transfer', requireHeader('X-Requested-With', 'XMLHttpRequest'), handleTransfer);

Remember

Key Takeaways

  • React auto-escapes rendered text by default — the real XSS risk is dangerouslySetInnerHTML or server-built raw HTML with unsanitized input.
  • Sanitize any user-provided HTML with a library like DOMPurify immediately before rendering it — never trust it as-is.
  • CSRF exploits automatic cookie attachment on cross-site requests — SameSite cookies + CSRF tokens + custom-header checks are the standard defenses.
  • Header-based (Authorization: Bearer) auth is naturally immune to CSRF, since a cross-site form cannot set custom headers.
  • SSL Pinning defends mobile apps against MITM even when the device's OS-level trust store has been compromised — at the cost of needing app updates to rotate pins.

Do It

Practice

  1. 1Deliberately render unsanitized user input via dangerouslySetInnerHTML in a sandboxed test app and confirm a <script> payload executes — then fix it with DOMPurify.
  2. 2Configure SameSite=strict on a test cookie and confirm (via a cross-origin test form) that it is withheld on the cross-site request.
  3. 3Read the OWASP Top 10 list and write one sentence per item connecting it to a concrete frontend/mobile scenario you could encounter.