Recently we announced the Internet Explorer 8 XSS Filter and talked a bit about its design philosophy. This post will describe the filter’s architecture and implementation in more detail.
Design Goals
The Internet Explorer 8 XSS Filter is intended to mitigate reflected / “Type-1” XSS vulnerabilities in a way that does not “break the web.” Our baseline approach needs to satisfy the following three conditions:
-
The XSS Filter must be compatible.
- There should be minimal, ideally zero, disruption to benign content/data. We might be able to achieve effective filtering if we were to drop all non-alphanumeric characters from input, however this would be an unrealistic and overbearing solution. Any solution that involves directly modifying request URLs is likely to persist corrupted data on the server-side. Similarly, approaches that would ask the user questions they can’t answer or block entire pages are not acceptable.
-
The XSS Filter must be secure.
- In general it must not be possible to subvert the filter by modifying attacks that are otherwise intentionally blocked. Although the XSS Filter cannot mitigate all possible XSS attacks, it can win some critical battles decisively. We can push as far as possible to maximize the XSS Filter’s effectiveness as long as we are also careful not to compromise compatibility or performance.
-
The XSS Filter must be performant.
- Users prefer a fast browser to a slow one, even if the slower one is “more secure.” So some approaches are simply not acceptable for performance reasons. For example, creating an extra instance of the browser rendering engine for each navigation would be too impactful to consider.
In implementing the filter, we made decisions to best meet the above goals.
Practical Considerations
The XSS Filter must be in a position to observe and intercept requests and responses from the browser to the web server. In Internet Explorer, this is possible via a MIME Filter. The prototype implementation of the XSS Filter was in fact implemented as a MIME Filter, but for performance it was moved into MSHTML (the browser rendering engine) when we built Internet Explorer 8.
Architecture / Implementation
Figure A: XSS Filter Hosted in Internet Explorer 8
Figure B: XSS Filter Logic Flow
Figures A and B depict a high level view of the XSS Filter. Let’s dig into the details.
For performance, the XSS Filter only takes effect for navigations within the browser which can result in the execution of script. There’s no need for the XSS Filter to operate on resources such as images (as long as they are truly images when rendered by Internet Explorer).
The filter also checks the source and destination URLs of navigations within the browser. If the navigation is cross-site, or the source cannot be determined (ex: the user clicked on a Favorite) then the navigation is filtered.
The XSS Filter can be enabled/disabled per-zone. For the Beta 2 release the XSS Filter will be enabled for the Internet and Restricted Sites zones, but not the Local Intranet zone. Administrators can choose to enable or disable the XSS Filter for any zone via group policy.
The core XSS Filter engine operates in two stages:
1. HTTP GET / POST data is scanned to match a set of _heuristics _that identify XSS attack vectors. Matches are used to build signatures to identify markup/script as replayed in the HTTP response.
2. Generated signatures are used to scan the HTTP response. Markup/script which has been identified by a signature is neutered to block execution.
Validating that an XSS attack has actually been replayed into the response maximizes XSS detection reliability – “reflected XSS” requires reflection. Having the capability to identify and neuter the replayed markup/script allows the filter to avoid overbearing mitigations such as querying the user, modifying outgoing requests, or blocking entire pages.
Our approach is performant in that the only notable “heavy lifting” is the scan of the HTTP response body, which will only occur in cases where signatures are generated. Signature generation is highly indicative of an actual XSS attack and it is rare during everyday browsing.
Fun with Regular Expressions – Part 1: Heuristics
If a navigation has met the criteria for filtering, the filter takes the URL as well as any POST data associated with the request, decodes it as necessary, and uses regular expressions to identify XSS attack vectors. These case-insensitive patterns are the filtering heuristics. Here is an example:
{<sc{r}ipt.*src*=}
This heuristic will identify SCRIPT tags with SRC attributes. While SCRIPT tags may be common in HTML, their presence in a URL or POST data is one indication of an XSS attack.
In the example heuristic above, note that the character within the inner braces is what we will refer to here as the neuter character. Each heuristic can have one or more neuter characters. Each neuter character, in this case ‘r’, indicates a character that will eventually be modified by the filter in the HTTP response body in order to block the XSS attack. The ‘#’ character is used as the neuter replacement character – it is effective in breaking HTML elements as well as script blocks into which it is injected.
The selection of neuter characters in any heuristic is very important. The wrong neuter characters chosen for a heuristic can subvert the filter. As an example, picking a quote symbol as a neuter character would cause the filter to neuter quotes. A smart attacker could use this behavior to force a match and neuter a quote on a page, intending to enable an XSS attack that wouldn’t otherwise be possible.
A match on a heuristic does not in and of itself trigger the filter to detect XSS. Rather, it indicates to the filter that the HTTP response body must be examined to validate that the script in the input URL or POST data was in fact replayed to the output page.
The decoding process briefly mentioned above is flexible and can also account for artifacts of various web platforms. As necessary, the filter generates additional signatures (see below) based on alternate interpretations of the same input data. So for example, because malformed URLEncoded characters may be handled differently for different web platforms, the filter must be capable of building proper signatures regardless.
Fun with Regular Expressions – Part 2: Signatures
As heuristics are matched, the filter generates one signature for each match. A signature is a new regular expression that will be used to scan the HTTP response body for the replayed suspect input. The neuter replacement character is temporarily put into place in the input after a signature is matched. Matching then continues for a heuristic until no more matches can be found in the input. Signatures are generated for URLs _without _neuter replacement characters in place. Otherwise the signatures would themselves contain neuter replacement characters and they would not correctly match attacks present in the HTTP response.
With each heuristic, a list of safe characters is provided. For the heuristic that detects script tags, the safe characters are the greater-than and less-than characters and also alpha-numerics. The safe characters effectively form the essence of the XSS attack the filter is attempting to identify.
Why signatures? |
---|
If the filter were to simply search for a match verbatim it would not necessarily find one. The web server may incidentally remove or translate particular characters from the request as it is replayed. It is in fact common and attackers can use this behavior to their advantage. |
Safe characters are restricted to the “low-ASCII” range (0x00 – 0x7F) so that we can essentially remain character-set agnostic. Character sets that are capable of alternate “low-ASCII” encodings (eg: UTF-7) are not currently special-cased, however some new restrictions are being placed on the general usage of these character sets moving forward. (These changes are outside the scope of this blog post but stay tuned to my blog for more details).
Here is an example match for the heuristic that detects script tags:
<SCRIPT src=”http://www.fabrikam.com/evil.js”
The signature generated for this match would be:
<SC{R}IPT¤src¤¤http¤¤¤www¤fabrikam¤com¤evil¤js¤
Each ¤ in the signature indicates a non-safe character from the original match. A sequence of zero to N unspecified characters will match any ¤. (Currently N is 10)
If no signatures have been generated for a particular page then the filter permits the page to load without modification – no XSS was detected.
However, if signatures do exist, the filter scans the HTTP response body for each signature. Once identified, the filter records exactly which character(s) must be neutered, as indicated in the signature by the characters within the braces. Once the signature list is fully processed the neuter replacement characters are put into place and the HTTP response body is passed on to render in the browser.
The page will render normally except the information bar will notify the user that the page was modified and the XSS attack will be disabled.
XSS Filter Limitations
Like all security mitigation and protection technologies, the XSS Filter’s approach does have limitations, being that it is a pragmatic balance between application compatibility, security, and performance. Some examples:
-
Injection into some contexts is not blocked. Ex: Scenarios where content can be injected directly into javascript without breaking out of a string.
-
-
Injections facilitated by some HTTP headers are not currently blocked. Ex: “Referer” based injection.
- If a page contains multiple nearby injection points, attacks can be constructed that thwart the XSS Filter.****
These are all issues that undoubtedly occur on real web sites. The XSS Filter design philosophy dictates that we make a distinction between issues that generally enable the XSS Filter to be bypassed vs. issues that apply only in certain situations. The issues above, while very notable, clearly fall into the latter category. As time goes on we will continue to enhance the XSS Filter to maximize its effectiveness, however we will not compromise web site compatibility in the process.**
**
Conclusion
It is challenging to mitigate XSS in a way that balances the needs of compatibility, security, and performance. The XSS Filter’s two-stage approach helps us achieve these goals by very specifically targeting reflected (“Type-1”) XSS attacks. This architecture allows us to mitigate the XSS most commonly found across the web today, by default, for users of Internet Explorer 8.
- David Ross, SVRD Blogger
*Postings are provided “AS IS” with no warranties, and confers no rights.*
August 19, 2008, 4:15pm: Updated with correct date