XSS filter evasion techniques allow attackers to bypass cross-site scripting (XSS) protections designed to block malicious scripts. This article explores some of the most common filter bypass strategies, explains why relying solely on filtering is ineffective, and outlines the best practices for preventing XSS attacks.

Attackers have developed hundreds of methods to evade XSS filters, making it clear that filtering alone is not a foolproof defense. For an XSS attack to succeed, two conditions must be met:

  1. The application must have an XSS vulnerability that allows user-controlled input to be injected into web pages.
  2. The attacker must find a way to execute malicious JavaScript within the victim’s browser.

XSS filtering aims to stop these attacks by detecting and removing suspicious code before it reaches the browser. However, because attackers continuously develop new techniques to disguise or encode their payloads, filtering alone cannot fully prevent XSS. Before exploring just a handful of the many ways filters can be bypassed, let’s first take a look at how XSS filtering works and why it remains an incomplete solution.

Why XSS Filtering Is Challenging and Often Ineffective

XSS filtering is a security mechanism designed to detect and block cross-site scripting (XSS) attempts by inspecting user input for potentially harmful scripts. Filtering can be implemented at different levels:

  • Client-side filtering happens in the browser before data is processed.
  • Server-side filtering occurs during request processing on the web server.
  • Web Application Firewalls (WAFs) analyze and block suspicious requests before they reach the application.

For many years, server-side filtering was the primary defense against XSS. Eventually, browser vendors introduced XSS auditors, which attempted to detect malicious scripts before rendering them. These auditors scanned incoming data for known XSS patterns, such as <script> tags in unexpected locations, using techniques like regular expressions and blacklists. When suspicious code was found, the browser could either block the entire page or remove only the detected script fragment—both of which had drawbacks and sometimes introduced new security risks. As a result, built-in browser XSS filters were eventually discontinued.

The Limitations of XSS Filtering

No single filtering approach can comprehensively prevent XSS attacks due to the diverse ways in which malicious scripts can be injected and executed:

  • Browser-based filtering is only effective against reflected XSS, where injected scripts are immediately reflected back in a browser response. However, it does not protect against stored XSS (where malicious scripts are saved in the database and executed later) or DOM-based XSS (where the exploit happens entirely within the browser without server involvement).
  • Server-side and WAF filtering can help mitigate reflected and stored XSS, but they cannot stop DOM-based XSS, since those attacks occur directly in the browser without ever reaching the server.
  • Application-level filtering is highly complex, requires ongoing updates to keep up with new attack techniques, and can sometimes introduce unintended security issues.

How Attackers Bypass Cross-Site Scripting (XSS) Filters

XSS filters are designed to block malicious scripts, but attackers have developed numerous evasion techniques to bypass them. While XSS attacks typically exploit application vulnerabilities and misconfigurations, filter evasion techniquestarget weaknesses in the filtering mechanisms of browsers, servers, or web application firewalls (WAFs).

At best, XSS filtering adds an extra layer of difficulty for attackers by forcing them to find ways to slip their payloads past security measures. However, filtering alone is not a foolproof defense, as attackers continuously find new ways to exploit gaps in web security mechanisms.

How XSS Filters Are Bypassed

Attackers take advantage of inconsistencies in how browsers interpret web code. Modern browsers spend a significant amount of processing power correcting and rendering malformed HTML, CSS, and JavaScript to ensure web pages display properly—even when there are coding errors. XSS filter evasion techniques abuse this complexity by exploiting variations in how different browsers handle non-standard code, exceptions, and edge cases.

Common Techniques Used to Evade XSS Filters

There are countless ways to bypass XSS filters, often involving obscured or unconventional script injection methods. While <script> tag injections are usually blocked, attackers frequently use alternative HTML elements and event handlers to execute malicious scripts.

Common evasion techniques include:

  • Using HTML event handlers: Instead of injecting <script> tags, attackers use attributes like onerror, onclick, or onfocus to execute JavaScript when a user interacts with the page.
  • Encoding techniques: Obfuscating payloads using different encoding methods (e.g., URL encoding, Base64 encoding) to slip past basic filters.
  • JavaScript quirks and syntax variations: Taking advantage of browser-specific parsing rules that allow JavaScript execution in unexpected ways.
  • Abusing malformed HTML: Injecting scripts into elements that browsers automatically attempt to “fix,” inadvertently executing the malicious code.

The Scale of XSS Filter Bypass Techniques

The number of ways to bypass XSS filters is staggering. Even the longest-known lists of filter bypass methods—such as the OWASP XSS Filter Evasion Cheat Sheet (which builds on RSnake’s original work)—only scratch the surface. While many bypass methods work only in specific scenarios, anyone working with JavaScript and web security should be aware that these exploits exist and that relying solely on filtering is not a reliable defense.

Character Encoding Tricks for XSS Evasion

Attackers often use character encoding techniques to bypass XSS filters that rely on detecting specific keywords or patterns. By encoding characters in different formats, they can obscure malicious payloads from basic filtering mechanisms. Additionally, nested encodings—where a string is encoded multiple times using different methods—can further evade detection.

The effectiveness of encoding tricks depends on how the browser processes and decodes characters in different contexts. For instance, URL encoding works within <a href> attributes but may not function the same way in other elements. Similarly, different encodings can be interpreted differently across browsers, making detection and prevention more difficult.

Example: Encoding to Evade JavaScript Detection

A basic XSS filter might block the javascript: keyword to prevent execution of inline scripts. However, an attacker can encode some or all of the characters using HTML entity encoding to obscure the payload:

<a href=”&#106;avascript:alert(‘Successful XSS’)”>Click this link!</a>

In this example, &#106; represents the ASCII character for “j”, meaning the browser will correctly interpret and execute the javascript: command once the page loads.

Why Encoding Tricks Are Dangerous

Character encoding tricks are especially effective because:

  • Filters may not decode input before scanning, allowing encoded payloads to slip through.
  • Browsers automatically decode and execute encoded values, making them a reliable attack vector.
  • Multiple encodings can be stacked, further complicating detection.

These techniques demonstrate why string-matching filters alone cannot effectively prevent XSS. A comprehensive security approach, including Content Security Policies (CSPs), input sanitization, and secure coding practices, is necessary to mitigate these threats.

Attackers use various encoding methods to obscure malicious payloads and bypass XSS filters that scan for specific patterns. Encoding techniques exploit how browsers interpret character representations, allowing scripts to execute despite being altered from their original form. Below are some of the most commonly used encoding techniques for evading XSS filters.

Hexadecimal Encoding for ASCII Characters

Some filters detect HTML entity codes by looking for patterns like &# followed by a number. To bypass this, attackers can use hexadecimal encoding for ASCII characters instead of decimal values:

<a href=”&#x6A;avascript:alert(document.cookie)”>Click this link!</a>

 

Here, &#x6A; represents the ASCII character “j”, allowing the browser to reconstruct the javascript: scheme.

Base64 Encoding for Obfuscation

Attackers can also use Base64 encoding to disguise payloads. When executed, JavaScript functions such as atob()decode the Base64 string, reconstructing the original malicious script:

<body onload="eval(atob('YWxlcnQoJ1N1Y2Nlc3NmdWwgWFNTJyk='))">


This decodes to:

alert('Successful XSS');

Zero-Padded Entity Variations

HTML entity encoding allows 1 to 7 numeric characters, and leading zeros are ignored. This means each character can have multiple valid representations, making it harder for filters to catch all variations. For example, the < character alone has at least 70 valid encodings, as listed in the OWASP XSS Filter Evasion Cheat Sheet. Additionally, semicolons are not required at the end of entity codes, further complicating detection:

<a href="&#x6A;avascript&#0000058&#0000097lert('Successful XSS')">Click this link!</a>

 

Here, &#x6A; represents "j", &#0000058; represents "X", and &#0000097; represents "a", ultimately forming a valid javascript:alert() payload.

Character Codes for Concealed Script Execution

Instead of writing a script directly, attackers can use JavaScript’s String.fromCharCode() function to generate it dynamically, making detection harder:

<iframe src=# onmouseover=alert(String.fromCharCode(88,83,83))></iframe>

 

In this example, String.fromCharCode(88,83,83) translates to “XSS”, triggering an alert when the mouse hovers over the iframe.

Why These Encoding Tricks Work

  • Many filters only scan for specific patterns, such as javascript: or <script>, and fail to decode obfuscated payloads.
  • Browsers automatically interpret encoded characters, reconstructing the malicious script before execution.
  • Multiple encoding methods can be combined, making detection even more difficult.

These examples highlight the limitations of XSS filtering and reinforce why stronger security measures, such as Content Security Policy (CSP), proper input validation, and output encoding, are essential for preventing XSS attacks.

Using Whitespace to Evade XSS Filters

Browsers are highly tolerant of whitespace variations in HTML and JavaScript, allowing attackers to use non-printing characters to bypass basic XSS filters. While most modern browsers have hardened against these techniques, whitespace embedding can still work in certain contexts where filtering mechanisms fail to anticipate these variations.

Breaking Up Keywords with Tabs

Browsers ignore tab characters when parsing JavaScript, meaning an attacker can insert tabs within keywords to evade simple string-matching filters. For example, an <img> tag with an XSS payload can be obfuscated as follows (though this method is ineffective in most modern browsers):

<img src="java script:al ert('Successful XSS')">

 

Alternatively, tabs can be encoded using hexadecimal representations to further obscure the payload:

 

<img src="java&#x09;script:al&#x09;ert('Successful XSS')">

Using Newlines and Carriage Returns

Similar to tabs, newline (\n, &#x0A;) and carriage return (\r, &#x0D;) characters are ignored when parsing JavaScript and HTML. Attackers can use these characters to split keywords, making detection more difficult:

<a href="jav&#x0A;ascript:&#x0A;ale&#x0D;rt('Successful XSS')">Visit google.com</a>

 

In this case, the browser correctly interprets the encoded characters, reconstructing the javascript: payload before execution.

Using Whitespace and Special Characters to Evade Filters

Some XSS filters specifically look for “javascript:” or ‘javascript:’, assuming that there will be no unexpected whitespace or special characters. However, browsers allow any combination of spaces and non-printable characters (ASCII values 1-32 in decimal) before recognized keywords, making it possible to bypass such filters:

<a href="  &#x8; &#23;   javascript:alert('Successful XSS')">Click this link!</a>

In this example:

  • &#x8; (ASCII backspace) and &#23; (ASCII device control character) are inserted before javascript: to disrupt simple keyword detection.
  • The browser still recognizes and executes the payload correctly.

Why These Techniques Work

  • Many filtering systems check for exact matches of known attack patterns but fail to account for whitespace and encoding variations.
  • Browsers automatically normalize and execute code, even when non-printable characters are inserted.
  • Attackers can combine multiple evasion techniques, making it difficult to create a universal filter that blocks all variations.

These examples highlight the limitations of filter-based defenses and reinforce why secure input validation, output encoding, and Content Security Policies (CSPs) are necessary to properly mitigate XSS vulnerabilities.

Manipulating Tags to Evade XSS Filters

Attackers can exploit how browsers process and repair malformed HTML to bypass XSS filters that simply scan for and remove certain tags. By nesting, omitting spaces, or breaking syntax in unconventional ways, they can craft payloads that slip through basic filtering mechanisms while remaining executable in the browser.

Nesting Tags to Evade Simple Tag Removal

If an XSS filter detects and removes <script> tags in a single pass, attackers can nest them within other tags to ensure that valid executable code remains after filtering:

<scr<script>ipt>document.write("Successful XSS")</scr<script>ipt>

 

In this example, if a filter removes <script>, the remaining code reconstructs a valid <script> tag, allowing the JavaScript to execute.

Omitting Whitespace and Using Slashes as Separators

Browsers do not always require spaces between attributes, and some filters fail to account for alternative character placements. A forward slash (/) can act as a separator between the tag name and attributes, avoiding detection:

<img/src=”funny.jpg”onload=javascript:eval(alert(‘Successful&#32XSS’))>

This entirely whitespace-free payload still executes correctly in a browser, as it recognizes the onload attribute despite the unconventional formatting.

Using SVG Tags for XSS Execution

Because modern browsers support SVG (Scalable Vector Graphics), attackers can use SVG elements as an alternative to <script> tags. This allows script execution even in environments that block traditional script tags:

<svg/onload=alert('XSS')>

 

Even if certain characters are restricted, like parentheses or single quotes, attackers can replace them with backticks (`), which are still valid in JavaScript:

 

<svg/onload=alert`XSS`>

 

Exploiting Browser Auto-Correction of Malformed Tags

Web browsers are designed to fix broken HTML to ensure web pages display properly. Attackers can take advantage of this behavior to craft malformed elements that become valid after processing.

For example, omitting the href attribute and quotes in an <a> tag still allows an event handler to execute JavaScript:

<a onmouseover=alert(document.cookie)>Go to google.com</a>

 

Extreme Example: Breaking and Reconstructing an <img> Tag

By deliberately corrupting an <img> tag’s syntax, attackers can rely on the browser’s automatic correction mechanismsto repair the tag and execute a malicious script:

<img """><script src=xssattempt.js></script>">

 

Once interpreted by the browser, the misplaced attributes are ignored, and the <script> tag executes as intended.

Why These Techniques Work

  • Filters that remove or block tags in a single pass may fail to prevent nested or reconstructed elements.
  • Browsers attempt to correct malformed HTML, allowing attackers to craft broken but ultimately functionalpayloads.
  • Alternative tag structures, such as SVG or event handler attributes, provide script execution pathways even in environments that block <script> tags.
  • Filters that expect standard formatting can be bypassed by removing spaces, using alternative separators, or encoding characters differently.

These examples highlight the importance of context-aware input validation, secure output encoding, and the use of Content Security Policies (CSPs) to prevent XSS vulnerabilities, rather than relying on simple tag-based filtering alone.

Exploiting Internet Explorer’s Unique XSS Vulnerabilities

Before the dominance of Chrome and Firefox—and long before Microsoft Edge—Internet Explorer (IE) was the primary web browser. Due to its non-standard implementations and deep integration with other Microsoft technologies, IE introduced unique security quirks that attackers could exploit. While modern browsers have largely moved past these vulnerabilities, some legacy enterprise applications still rely on IE-specific features, making them relevant for certain attack scenarios.

VBScript Execution in Older Internet Explorer Versions

Most XSS filters are designed to block JavaScript-based payloads, but versions of Internet Explorer up to IE10 also supported VBScript, creating an alternative attack vector:

<a href='vbscript:MsgBox("Successful XSS")'>Click here</a>

 

Because VBScript execution was allowed in hyperlinks, attackers could trigger malicious actions through simple anchor tags.

CSS-Based Script Execution with Dynamic Properties

Internet Explorer introduced dynamic properties, which allowed CSS attributes to execute JavaScript expressions. This behavior provided an unusual XSS entry point via CSS:

body { color: expression(alert('Successful XSS')); }

 

Instead of acting as a pure styling rule, expression() could evaluate and execute arbitrary JavaScript when the affected CSS property was accessed.

Using the dynsrc Attribute for Script Execution

IE also supported the now-removed dynsrc attribute for images, which could be abused to execute JavaScript:

<img dynsrc="javascript:alert('Successful XSS')">

 

Unlike standard <img> tags, which require properly formatted image sources, IE would process dynsrc as a valid JavaScript URL, triggering execution.

Bypassing Quote Restrictions with Backticks

If an application restricted both single (‘) and double (“) quotes, attackers could use backticks (`) instead, which Internet Explorer interpreted correctly in certain cases:

<img src=`javascript:alert("The name is 'XSS'")`>

Disguising a Script as an External Stylesheet

Older versions of Internet Explorer allowed scripts to be executed by embedding them in external CSS files. Attackers could exploit this by injecting malicious code into a .css file and referencing it in a <link> tag:

<link rel="stylesheet" href="http://example.com/xss.css">

 

If the external file contained JavaScript instead of CSS, some versions of IE would still execute the script, providing another method for delivering XSS payloads.

Why These Techniques Matter

While most of these IE-specific vulnerabilities have been eliminated in modern browsers, some legacy enterprise systems still rely on older versions of IE. Attackers targeting these environments can use these non-standard execution methods to bypass traditional XSS filters.

To properly mitigate these risks, security measures should include:

  • Strict Content Security Policies (CSPs) to prevent execution of unauthorized scripts.
  • Input validation and output encoding to block injection attempts at the application level.
  • Eliminating reliance on outdated browsers and enforcing secure, modern alternatives.

Despite being historical quirks, these Internet Explorer exploits highlight how browser-specific behaviors can create unexpected security vulnerabilities.

A Look Back: Legacy XSS Exploits

As web technologies evolve, XSS filter bypass techniques quickly become outdated. However, looking at past exploits provides insight into the unintended edge cases that emerge when new specifications are introduced while maintaining backward compatibility. Below are some historical XSS techniques that may no longer work in modern browsers but highlight the creative methods attackers have used in the past.

Injecting JavaScript into Background Attributes

In older browsers, HTML attributes meant for styling could be used to execute JavaScript. For example, the background attribute on a <body> tag could trigger an XSS payload:

<body background="javascript:alert('Successful XSS')">

 

A similar approach worked when using inline CSS properties, injecting JavaScript into a background-image property:

 

<div style="background-image:url(javascript:alert('Successful XSS'))">

Images as Script Execution Vectors

Some browsers allowed non-image content to be executed when loaded into an <img> or <input> field. Attackers could exploit this by using a JavaScript URL in an image source:

<input type="image" src="javascript:alert('Successful XSS')">

Using <meta> Refresh for Script Injection

In some outdated browsers, the <meta> tag’s refresh attribute could be abused to redirect the page to a Base64-encoded JavaScript payload, leading to execution:

<meta http-equiv="refresh" content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgnWFNTJyk8L3NjcmlwdD4K">

 

Here, the Base64-encoded string decodes into:

 

<script>alert('XSS')</script>

 

This technique worked because some browsers automatically decoded and executed Base64-encoded content as part of a page load.

UTF-7 Encoding: A Forgotten XSS Attack

At one point, it was possible to hide XSS payloads using UTF-7 encoding, a character encoding format that some browsers supported:

<head><meta http-equiv="content-type" content="text/html; charset=utf-7"></head>

+adw-script+ad4-alert('xss');+adw-/script+ad4-

In this example:

  • The <meta> tag declares that the page uses UTF-7 encoding instead of the more common UTF-8.
  • The XSS payload is encoded using UTF-7 notation, where +adw- represents < and +ad4- represents >.
  • Some browsers automatically interpreted and executed this encoded JavaScript, allowing for successful exploitation.

Why These Methods Matter

While these specific techniques no longer work in modern browsers, they illustrate how small quirks in web technology can lead to major security issues. Attackers continually look for unexpected behaviors in browsers and web standards, which is why XSS prevention must go beyond filtering and include:

  • Proper input validation and output encoding to neutralize potential attack vectors.
  • Content Security Policies (CSPs) to restrict script execution sources.
  • Regular security updates to prevent exploitation of older browser features.

Even though these methods are historical artifacts, they serve as a reminder that security must evolve alongside web technologies to prevent future XSS vulnerabilities.

How to Protect Your Applications from XSS – Beyond Filtering

While web application firewalls (WAFs) can provide some level of XSS filtering, they should only be considered one layer of a broader security strategy. With hundreds of known filter bypass techniques and new attack methods constantly emerging, filtering alone is not a reliable defense. Additionally, aggressive filtering can interfere with legitimate scripts, which is one of the reasons browser vendors are moving away from built-in XSS filtering.

Building Secure Applications to Prevent XSS

The most effective way to protect against cross-site scripting (XSS) is to write secure, well-structured code that prevents vulnerabilities at the source. Instead of relying on filters, developers should:

  • Treat all user input as untrusted by default, ensuring strict validation and sanitization.
  • Apply context-aware escaping and encoding, making sure that user-controlled data cannot be interpreted as executable code.
  • Enforce security at the HTTP level by implementing Content Security Policy (CSP) headers and other essential HTTP security headers to restrict script execution.

Continuous Testing for XSS Prevention

Even with secure coding practices in place, applications must be regularly tested to ensure that new features, updates, or configuration changes do not introduce XSS vulnerabilities. A robust web vulnerability scanning process should be integrated into development workflows, allowing for continuous security monitoring and automated detection of misconfigurations and vulnerabilities.

By prioritizing secure development practices, proper security headers, and ongoing vulnerability assessments, developers can effectively mitigate XSS threats without relying on filtering alone.

SHARE THIS POST
THE AUTHOR
Acunetix

Acunetix developers and tech agents regularly contribute to the blog. All the Acunetix developers come with years of experience in the web security sphere.