Posts Tagged ‘XSS’
Guarding the Web: Understanding and Mitigating Modern Application Security Risks: XSS • CSRF • Clickjacking • CORS • SSRF
Modern web applications operate in a hostile environment. Attackers exploit input handling, browser trust, and server connectivity to inject code (XSS), trigger unauthorized actions (CSRF), trick users into clicking hidden UI (clickjacking), abuse permissive cross-origin policy (CORS), or make the server itself fetch sensitive resources (SSRF). This article explains the mechanics of each threat and provides concrete, production-grade mitigation patterns you can adopt today.
Cross-Site Scripting (XSS): When Untrusted Input Becomes Code
Threat model: User-provided data is rendered into a page without correct encoding or policy restrictions, allowing script execution in the victim’s browser.
Typical Exploits
Stored XSS (comments, profiles):
<script>fetch('/api/session/steal', {credentials:'include'})</script>
Reflected XSS (search query):
GET /search?q=<script>alert(1)</script>
DOM-based XSS (dangerous sinks):
// Anti-pattern: innerHTML with untrusted input
const result = new URLSearchParams(location.search).get('msg');
document.getElementById('banner').innerHTML = result; // XSS sink
High-Confidence Mitigations
- Contextual Output Encoding: Encode before insertion, based on context (HTML, attribute, URL, JS, CSS).
- Prefer Safe APIs: Use
textContent/setAttributeoverinnerHTML; avoideval/Function constructors. - Framework Defaults: Modern templating (e.g., React, Angular) auto-escapes. Avoid escape bypasses (e.g.,
dangerouslySetInnerHTML) unless you sanitize with robust libraries. - Content Security Policy (CSP): Block inline scripts and restrict sources; use nonces.
CSP Example (Nonce-Based)
Content-Security-Policy: default-src 'none';
script-src 'self' 'nonce-r4nd0mNonce';
style-src 'self';
img-src 'self' data:;
connect-src 'self';
base-uri 'self';
frame-ancestors 'self';
form-action 'self';
Server/Framework Snippets
Express.js: encode + CSP headers
app.use((req, res, next) => {
res.set('Content-Security-Policy',
"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'self'; base-uri 'self'; frame-ancestors 'self'");
res.set('X-Content-Type-Options', 'nosniff');
next();
});
Do sanitize if you must render HTML: Use a vetted sanitizer (e.g., DOMPurify on the client, OWASP Java HTML Sanitizer on the server) with an allowlist.
Cross-Site Request Forgery (CSRF): Abusing Session Trust
Threat model: A logged-in user’s browser sends a forged state-changing request (e.g., money transfer) to a trusted site because cookies are automatically included.
Exploit Example
<form action="https://bank.example/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
Mitigations That Work Together
- Synchronizer (Anti-CSRF) Tokens: Embed a per-session/per-request token in each state-changing form or AJAX request; verify server-side.
- SameSite Cookies: Set session cookies to
SameSite=LaxorStrictandSecureto block cross-site inclusion. - Method & Content Checks: Require POST/PUT/DELETE with
Content-Type: application/json; reject unexpected content types. - Origin/Referer Validation: For sensitive endpoints, verify
Origin(preferred) orReferer. - Re-authentication / Step-Up: For high-value actions, require a second factor or password confirmation.
Example (Express + CSRF Token)
// Pseudocode with csurf-like middleware
app.post('/transfer', requireAuth, verifyCsrfToken, (req, res) => {
// process transfer
});
SPA + API note: Prefer token-based auth (Authorization header) to avoid ambient cookies. If you must use cookies, combine SameSite with CSRF tokens.
Clickjacking: UI Redress and Hidden Frames
Threat model: An attacker overlays your site in an invisible <iframe> and tricks users into clicking sensitive controls.
Exploit Sketch
<iframe src="https://target.example/approve" style="opacity:0;position:absolute;top:0;left:0;width:100%;height:100%"></iframe>
Mitigations
- X-Frame-Options (XFO):
DENYorSAMEORIGIN. (Legacy but still widely respected.) - CSP frame-ancestors: Modern, fine-grained embedding control.
- UI Hardening: Re-prompt for confirmation on dangerous actions; disable one-click irreversible changes.
Header Examples
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'self'
Server Config
Nginx:
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "frame-ancestors 'self'" always;
Apache:
Header always set X-Frame-Options "DENY"
Header always set Content-Security-Policy "frame-ancestors 'self'"
CORS: Safe Cross-Origin Requests Without Overexposure
Threat model: Overly permissive CORS allows arbitrary origins to read sensitive API responses or send authenticated requests.
Dangerous Patterns
Access-Control-Allow-Origin: *combined withAccess-Control-Allow-Credentials: true(browsers will ignore, but this signals confusion and often pairs with other mistakes).- Reflecting the request
Originwholesale without an allowlist. - Forgetting
Vary: Origin, causing caches/CDNs to serve one origin’s CORS response to all.
Safe Configuration
Access-Control-Allow-Origin: https://app.example
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Authorization, Content-Type
Access-Control-Expose-Headers: ETag, X-Request-Id
Vary: Origin
Implementation Tip
// Pseudocode: strict allowlist
const allowed = new Set(['https://app.example', 'https://admin.example']);
const origin = req.headers.origin;
if (allowed.has(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
}
Principle: CORS is not an auth mechanism—treat it as a read-permission gate. Apply per-endpoint scoping; don’t turn it on globally.
Server-Side Request Forgery (SSRF): Turning Your Server Into a Proxy
Threat model: The application fetches remote resources based on user input (URLs), allowing attackers to reach internal networks or cloud metadata endpoints.
Exploit Examples
- Cloud metadata theft:
http://169.254.169.254/latest/meta-data/(AWS) /http://metadata.google.internal//http://169.254.169.254/metadata/instance(Azure; IMDSv2/headers required). - Reaching services on
localhostor RFC1918 IPs (e.g., Redis, internal dashboards). - Schema abuse:
file://,gopher://, orftp://if unsupported schemas aren’t blocked. - Open redirect chains or DNS rebinding that convert a safe-looking hostname into an internal IP at resolution time.
Defense-in-Depth
- Strict Allowlists: Only permit fetching from specific hosts/paths; reject everything else.
- URL & IP Validation: Parse the URL; resolve DNS; block private and link-local ranges; re-validate after redirects.
- Egress Proxy with ACL: Force all outbound HTTP(S) through a proxy that enforces destination policies and logs requests.
- Cloud Hardening: Require IMDSv2 (AWS); block server access to metadata endpoints unless strictly needed.
- Timeouts & Size Limits: Short connect/read timeouts and response byte caps to prevent internal scanning and DoS.
Hardening Snippet (Pseudocode)
function safeFetch(userUrl) {
const url = new URL(userUrl);
// allowlist scheme
if (!['https:'].includes(url.protocol)) throw new Error('Blocked scheme');
// DNS resolve and block private IPs
const ip = resolveToIP(url.hostname);
if (isPrivate(ip) || isLinkLocal(ip) || isLoopback(ip)) throw new Error('Blocked destination');
// enforce egress proxy
return proxyHttpGet(url, { timeoutMs: 2000, maxBytes: 1_000_000, followRedirects: 3, revalidateIPOnRedirect: true });
}
Header note: SSRF is best mitigated by network policy and URL validation; “headers” alone can’t prevent SSRF, but you may set request policies (e.g., disallow redirects, strip sensitive headers) and enforce gateway rules.
Complementary Security Headers
- Strict-Transport-Security:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload - X-Content-Type-Options:
nosniffto block MIME type sniffing. - Referrer-Policy: e.g.,
strict-origin-when-cross-origin. - Permissions-Policy: Restrict powerful APIs (camera, geolocation, etc.).
- Cross-Origin-Opener-Policy / Resource-Policy / Embedder-Policy: Strengthen isolation where feasible.
Operational Controls & SDLC Integration
- Threat Modeling: Map trust boundaries (browser ⇄ app ⇄ services ⇄ internet) and identify high-risk data flows.
- Static/Dynamic Testing: SAST, DAST, and dependency scanning in CI; add security unit tests for templating and request flows.
- Observability: Log security-relevant events (CSP violations via
report-to, WAF blocks, CSRF failures, SSRF proxy denies) and alert. - WAF/Gateway Policies: Enforce header baselines, block known bad payloads, and constrain egress with explicit ACLs.
- Secrets Hygiene: Short-lived tokens, mTLS for service-to-service, and key rotation.
- Least Privilege by Default: Scope CORS per endpoint, narrow IAM roles, network segmentation.
Deployment Checklist
- ✅ XSS: Contextual encoding, CSP with nonces, sanitizer for any HTML rendering.
- ✅ CSRF: Anti-CSRF tokens,
SameSite+Securecookies, Origin checks, step-up auth on critical actions. - ✅ Clickjacking:
X-Frame-Options: DENYandCSP frame-ancestors 'self'. - ✅ CORS: Explicit allowlist, credentials only when necessary,
Vary: Origin, scoped to sensitive endpoints. - ✅ SSRF: Allowlist destinations, block internal ranges, egress proxy with ACL, timeouts/size caps, re-validate after redirects.
- ✅ Headers: HSTS, XCTO, Referrer-Policy, Permissions-Policy in place.
- ✅ Ops: Logging/alerting on policy violations; SAST/DAST integrated; WAF rules tuned to app.
[DotSecurity2017] Counter-spells and the Art of Keeping Your Application Safe
In the arcane atelier of application assurance, where user whims whirl into wicked whimsy, wielding wards against web’s wicked whims demands diligence and dexterity. Ingrid Epure, a frontend alchemist at Intercom, invoked this incantation at dotSecurity 2017, transmuting tales of Ember’s exigencies into elixirs for Ember’s endurance. A Romanian expatriate ensconced in Dublin’s digital demesne, Ingrid’s immersion—four-year Ember opus, Rails’ rearward rampart—yields yarns of 55 scribes scripting 2,000 shifts, 100 deploys diurnal.
Ingrid’s invocation opened with Intercom’s incantus: real-time runes for messaging’s mosaic, 250 commits cascading 30K additions—vulnerabilities’ vortex in velocity’s vortex. XSS’s xanthic xanthoma: inline sorcery (Ember’s {{}} incantations) inviting injection’s infestation—’s sorcery, CSP’s countercharm. Ingrid illuminated Ember’s ember: helpers’ hygiene (HTML-escapers’ aegis), bindings’ bulwark (triple braces’ taboo). Tools’ talisman: npm’s audit, ember-cli’s eldritch eyes—vulnerabilities’ vigil, dependencies’ divination.
CSRF’s chicanery: Ember’s CSRF tokens, Rails’ requiem—double-submit’s duality, synchronizer’s sentinel. Ingrid invoked interceptors: Ember’s data’s dominion, Rails’ requital. Content Security Policy’s codex: v2’s vigilance (nonces’ nebula, hashes’ heraldry), v3’s valor—scripts’ scrutiny, inline’s inquisition. Ingrid’s imprecation: Ember addon’s aegis, Node’s nexus—alerts’ alarum, anomalies’ augury.
This conjury: clean code’s creed, tools’ tome—CSP’s citadel, vulnerabilities vanquished.
Vulnerabilities’ Vortex and Wards’ Weave
Ingrid invoked Intercom’s incantus: Ember’s exigencies, XSS’s xanthoma—helpers’ hygiene, bindings’ bulwark.
CSRF’s Chicanery and CSP’s Codex
Tokens’ talisman, interceptors’ insight—v2’s vigilance, v3’s valor. Ingrid’s imprecation: addon’s aegis, Node’s nexus.
Links:
EN_DotSecurity2017_006_009.md