XSS
Overview
What is XSS
Cross-site scripting (XSS) is a code injection attack that allows an attacker to execute malicious JavaScript in another user’s browser.
The attacker exploits a vulnerability in a website, in order to get the website to deliver the malicious JavaScript for the victim to visit.
How the malicious JavaScript is injected
In the example below, a simple server-side script is used to display the latest comment on a website:
print "<html>"
print "Latest comment:"
print database.latestComment
print "</html>"
Since the user input is included directly, an attacker could submit this comment: ””. Any user visiting the page would now receive the following response:
<html>
Latest comment:
<script>...</script>
</html>
When the user’s browser loads the page, it will execute whatever JavaScript code is contained inside the <script>
tags. The attacker has now succeeded with his attack.
What is malicious JavaScript?
JavaScript runs in a very restricted environment that has extremely limited access to the user’s files and operating system. It would be very unlikely to cause any damage to your computer.
However, the possibility of JavaScript being malicious becomes more clear when you consider the following facts:
- JavaScript has access to some of the user’s sensitive information, such as cookies.
- JavaScript can send HTTP requests with arbitrary content to arbitrary destinations by using XMLHttpRequest and other mechanisms.
- JavaScript can make arbitrary modifications to the HTML of the current page by using DOM manipulation methods.
The consequences of malicious JavaScript
Cookie theft
The attacker can access the victim’s cookies associated with the website using document.cookie
, send them to his own server, and use them to extract sensitive information like session IDs.
Keylogging
The attacker can register a keyboard event listener using addEventListener
and then send all of the user’s keystrokes to his own server, potentially recording sensitive information such as passwords and credit card numbers.
Phishing
The attacker can insert a fake login form into the page using DOM manipulation, set the form’s action attribute to target his own server, and then trick the user into submitting sensitive information.
XSS Attacks
How attack works
Types of XSS
- Persistent XSS, where the malicious string originates from the website’s database.
- Reflected XSS, where the malicious string originates from the victim’s request.
- DOM-based XSS, where the vulnerability is in the client-side code rather than the server-side code.
Reflected XSS
In a reflected XSS attack, the malicious string is part of the victim’s request to the website.
How can reflected XSS succeed?
At first, reflected XSS might seem harmless because it requires the victim himself to actually send a request containing a malicious string. As it turns out, there are at least two common ways of causing a victim to launch a reflected XSS attack against himself:
- If the user targets a specific individual, the attacker can send the malicious URL to the victim (using e-mail or instant messaging, for example) and trick him into visiting it.
- If the user targets a large group of people, the attacker can publish a link to the malicious URL (on his own website or on a social network, for example) and wait for visitors to click it.
DOM-based XSS
What makes DOM-based XSS different
- In traditional XSS, the malicious JavaScript is executed when the page is loaded, as part of the HTML sent by the server.
- In DOM-based XSS, the malicious JavaScript is executed at some point after the page has loaded, as a result of the page’s legitimate JavaScript treating user input in an unsafe way.
Preventing XSS
Recall that an XSS attack is a type of code injection, In order to prevent this type of code injection, secure input handling is needed. For a web developer, there are two fundamentally different ways of performing secure input handling.
- Encoding, which escapes the user input so that the browser interprets it only as data, not as code.
- Validation, which filters the user input so that the browser interprets it as code without malicious commands.
Input handling contexts
||||
|:—:|:—:|
|Application code| <input value="userInput">
|
|Malicious string| "><script>...</script><input value="
|
|Resulting code| <input value=""><script>...</script><input value="">
|
If the same input were inserted into another context, the closing delimiter would be different and injection would become possible. For this reason, secure input handling always needs to be tailored to the context where the user input will be inserted.
Inbound/outbound input handling
There is no easy way of determining when user input arrives which context it will eventually be inserted into, Relying on inbound input handling to prevent XSS is thus a very brittle solution that will be prone to errors. Instead, outbound input handling should be your primary line of defense against XSS, because it can take into account the specific context that user input will be inserted into.
In most modern web applications, user input is handled by both server-side code and client-side code. In order to protect against all types of XSS, secure input handling must be performed in both the server-side code and the client-side code.
Encoding
Encoding is the act of escaping user input so that the browser interprets it only as data, not as code.
Limitations of encoding
Even with encoding, it will be possible to input malicious strings into some contexts.
document.querySelector('a').href = userInput
This in itself does not prevent the attacker from inserting a URL beginning with “javascript:“. When the link is clicked, whatever JavaScript is embedded inside the URL will be executed.
Validation
Validation is the act of filtering user input so that all malicious parts of it are removed, without necessarily removing all code in it. One of the most recognizable types of validation in web development is allowing some HTML elements (such as <em>
and <strong>
) but disallowing others (such as <script>
).
There are two main characteristics of validation that differ between implementations:
- Classification strategy
- Validation outcome
Classification strategy
Blacklisting
If a string matches this pattern, it is then marked as invalid.
However, blacklisting has two major drawbacks:
-
Complexity
Accurately describing the set of all possible malicious strings is usually a very complex task.
-
Staleness
Even if a perfect blacklist were developed, it would fail if a new feature allowing malicious use were added to the browser.
Whitelisting
Whitelisting is essentially the opposite of blacklisting: instead of defining a forbidden pattern, a whitelist approach defines an allowed pattern and marks input as invalid if it does not match this pattern.
Compared to blacklisting, there are two major benefits of whitelisting:
-
Simplicity
Accurately describing a set of safe strings is generally much easier than identifying the set of all malicious strings.
-
Longevity
Unlike a blacklist, a whitelist will generally not become obsolete when a new feature is added to the browser.
Validation outcome
When input has been marked as invalid, one of two actions can be taken:
-
Rejection The input is simply rejected
-
Sanitisation All invalid parts of the input are removed, and the remaining input is used normally by the website.
Which prevention technique to use
Encoding should be your first line of defense against XSS
As a second line of defense, you should use inbound validation to sanitize or reject data
As a third line of defense, you should also make use of Content Security Policy (CSP)
Content Security Policy (CSP)
CSP is used to constrain the browser viewing your page so that it can only use resources downloaded from trusted sources.
CSP can be used to enforce the following rules:
-
No untrusted sources
External resources can only be loaded from a set of clearly defined trusted sources.
-
No inline resources
Inline JavaScript and CSS will not be evaluated.
-
No eval
The JavaScript eval function cannot be used.
How to enable CSP
By default, browsers do not enforce CSP. To enable CSP on your website, pages must be served with an additional HTTP header: Content‑Security‑Policy
.
An example policy
Content‑Security‑Policy:
script‑src 'self' scripts.example.com;
media‑src 'none';
img‑src *;
default‑src 'self' http://*.example.com
- Scripts can be downloaded only from the host serving the page and from
scripts.example.com
. - Audio and video files cannot be downloaded from anywhere.
- Image files can be downloaded from any host.
- All other resources can be downloaded only from the host serving the page and from any subdomain of
example.com
.
Status of CSP
As of June 2013, Content Security Policy is a W3C candidate recommendation. It is being implemented by browser vendors, but parts of it are still browser-specific.
Summary
Overview of XSS
- XSS is a code injection attack made possible through insecure handling of user input.
- A successful XSS attack allows an attacker to execute malicious JavaScript in a victim’s browser.
- A successful XSS attack compromises the security of both the website and its users.
XSS Attacks
- There are three major types of XSS attacks:
- Persistent XSS, where the malicious input originates from the website’s database.
- Reflected XSS, where the malicious input originates from the victim’s request.
- DOM-based XSS, where the vulnerability is in the client-side code rather than the server-side code.
- All of these attacks are performed in different ways but have the same effect if they succeed.
Preventing XSS
- The most important way of preventing XSS attacks is to perform secure input handling.
- Most of the time, encoding should be performed whenever user input is included in a page.
- In some cases, encoding has to be replaced by or complemented with validation.
- Secure input handling has to take into account which context of a page the user input is inserted into.
- To prevent all types of XSS attacks, secure input handling has to be performed in both client-side and server-side code.
- Content Security Policy provides an additional layer of defense for when secure input handling fails.