Problem: Untrusted user input is injected into the DOM and interpreted as executable JavaScript. Attackers can steal cookies, tokens, or perform actions on behalf of the user.
- Reflected XSS: payload comes from the current request (URL, form).
- Stored XSS: payload is saved in the database and served to many users.
- DOM-based XSS: client-side JS builds unsafe HTML from user input.
Fix: never trust input. Use proper escaping/sanitization, avoidinnerHTML with raw data, and combine with a strictContent-Security-Policy.
Problem: Without CSP, the browser will execute any script the page loads, including injected ones.
Fix: define a CSP header that restricts where scripts, styles, and other resources can be loaded from. Example (very simplified):
Content-Security-Policy: default-src 'self'; script-src 'self';
- default-src: base policy for all resource types.
- script-src: where scripts are allowed to load from.
- style-src, img-src, connect-src: fine-tune other resource types.
Problem: The browser automatically sends cookies with cross-site requests. An attacker can trick the user into sending a state-changing request to your app while authenticated.
- Classic CSRF: malicious form or image triggers a POST to your app.
- Modern variant: combined with XSS for more powerful attacks.
Fix: use anti-CSRF tokens and configure cookies withSameSite and Secure. Example:
Set-Cookie: session=...; SameSite=Lax; Secure; HttpOnly
Problem: Cookies are sent with cross-site requests by default, enabling CSRF if not restricted.
Fix: set the SameSite attribute to control when cookies are attached:
- Lax: sent on top-level navigations (safe default for many apps).
- Strict: only same-site requests (strongest CSRF protection).
- None: cross-site allowed, but must be
Secure.
Set-Cookie: session=...; SameSite=Strict; Secure; HttpOnly
Problem: Users may access your site over plain HTTP or be downgraded by attackers, exposing traffic to interception.
Fix: send an HSTS header to force HTTPS for future requests:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- max-age: how long the browser should enforce HTTPS.
- includeSubDomains: apply to all subdomains.
- preload: opt-in to browser preload lists (with extra steps).
Problem: Third-party scripts or styles hosted on CDNs can be tampered with. If you load them blindly, you trust whatever is served.
Fix: use SRI to ensure the resource matches a known hash:
<script src="https://cdn.example.com/lib.js"
integrity="sha384-BASE64_HASH"
crossorigin="anonymous"></script>
- integrity: expected hash of the resource.
- crossorigin: required for some SRI scenarios.