Back to Security
Security
medium
mid

How do you ensure secure handling of sensitive user data on the client side?

Minimize what reaches the client, never store secrets in localStorage, use HttpOnly+Secure+SameSite cookies for tokens, enforce HTTPS, prevent XSS (sanitize, CSP, framework escaping), avoid logging sensitive data, mask in the UI, and remember the client is never a trust boundary.

7 min read·~12 min to think through

The foundational principle: the client is not a trust boundary. Anything in the browser is inspectable by the user, by malicious extensions, and — if you have an XSS hole — by an attacker. So: minimize, protect, and never rely on client-side enforcement.

1. Minimize — don't send what you don't need

  • Only ship the fields the UI actually renders. Don't return full user records, internal flags, password hashes, or other users' data "just in case."
  • No secrets, API keys, or credentials in client code or the bundle — they're public.
  • Keep sensitive data in memory only as long as needed; don't persist it casually.

2. Tokens & auth storage

  • Don't put auth tokens in localStorage — fully readable by any XSS payload.
  • Prefer HttpOnly + Secure + SameSite cookies — JS can't read them, so XSS can't exfiltrate them; SameSite mitigates CSRF.
  • Or token in memory + a silent-refresh flow — gone on reload, never persisted.
  • Short token lifetimes; rotate; logout actually invalidates server-side.

3. Transport

  • HTTPS everywhere — HSTS, no mixed content. Encrypts data in transit.
  • Sensitive data in request bodies, not URLs/query strings (URLs land in logs, history, referrers).

4. XSS is the main client-side threat to data

XSS = attacker JS running in your origin = it can read anything the page can.

  • Escape/encode output — rely on the framework (React escapes by default); avoid dangerouslySetInnerHTML / innerHTML with untrusted data, and sanitize (DOMPurify) if you must.
  • Content Security Policy — restrict script sources; blocks injected/inline scripts.
  • Validate and sanitize all inputs; be careful with href/src (javascript: URLs).

5. Don't leak it

  • Never log sensitive data — not to the console, not to error trackers (scrub PII before sending to Sentry/etc.), not to analytics.
  • Mask in the UI — show ****1234, password fields, reveal-on-demand.
  • Disable autocomplete where appropriate; beware browser extensions and screen-sharing.
  • Be careful what you cache (service workers, HTTP cache) for sensitive responses.

6. The trust boundary

  • All validation and authorization happens server-side. Client-side checks are UX only. Never trust client-sent data; never gate access purely in the client.
  • The server decides what each user is allowed to see and do — period.

7. Compliance & hygiene

  • Know the rules for the data (GDPR/PCI/HIPAA as applicable) — consent, retention, right-to-delete.
  • npm audit / dependency scanning — a compromised dependency can steal data.
  • Subresource Integrity for third-party scripts; limit third-party scripts (they run in your origin).

The one-liner

"Treat the client as hostile territory: send the minimum, never store secrets or tokens in JS-readable storage, enforce HTTPS, lock down XSS with framework escaping + CSP + sanitization, never log or cache sensitive data, mask it in the UI — and remember all real authorization lives on the server."

Follow-up questions

  • Why is localStorage a bad place for auth tokens?
  • How does an HttpOnly cookie defend against XSS token theft?
  • What is CSP and how does it reduce XSS impact?
  • Why is client-side validation never a security control?

Common mistakes

  • Storing JWTs/secrets in localStorage where XSS can read them.
  • Putting API keys or secrets in client code.
  • Logging PII/tokens to the console or error trackers.
  • Treating client-side validation/auth checks as security.
  • Using dangerouslySetInnerHTML with unsanitized content.

Performance considerations

Edge cases

  • Malicious browser extensions reading page data.
  • Sensitive data accidentally cached by a service worker.
  • PII leaking into URLs, referrers, or analytics events.
  • A compromised third-party script running in your origin.

Real-world examples

  • Auth via HttpOnly+Secure+SameSite cookie with short-lived tokens and silent refresh.
  • DOMPurify + a strict CSP on a page that must render user-generated HTML.
  • Scrubbing PII from error payloads before they reach Sentry.

Senior engineer discussion

Seniors anchor on 'the client is not a trust boundary' and reason from there: minimize data sent, keep tokens out of JS-readable storage (HttpOnly cookies or in-memory), treat XSS as the primary data-exfiltration vector (escaping + CSP + sanitization), never log/cache sensitive data, and put all real authorization on the server. They also raise dependency/third-party-script risk and compliance.

Related questions