Recent Posts
Archives

Posts Tagged ‘CORS’

PostHeaderIcon 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 / setAttribute over innerHTML; avoid eval/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=Lax or Strict and Secure to 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) or Referer.
  • 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): DENY or SAMEORIGIN. (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 with Access-Control-Allow-Credentials: true (browsers will ignore, but this signals confusion and often pairs with other mistakes).
  • Reflecting the request Origin wholesale 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 localhost or RFC1918 IPs (e.g., Redis, internal dashboards).
  • Schema abuse: file://, gopher://, or ftp:// 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: nosniff to 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 + Secure cookies, Origin checks, step-up auth on critical actions.
  • Clickjacking: X-Frame-Options: DENY and CSP 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.
Security is not a single feature; it is a posture expressed through code, configuration, and continuous verification.