Cross-Site Scripting (XSS)

In a Cross-Site Scripting (XSS), an attacker injects malicious code (e.g., HTML, Javascript) into web applications in order to leak sensitive information retained by the clients’ browsers accessing that site. Some examples of sensitive information are cookies and session tokens. XSS allows the attacker to bypasses the Same Origin Policy (SOP), since the injected code can directly access any information (including session cookies) of the vulnerable page.

From wikipedia:

Cross-site scripting flaws surpassed buffer overflows to become the most common publicly reported security vulnerability, with some researchers in 2007 viewing as many as 68% of websites as likely open to XSS attacks.

There are three well known types of XSS vulnerability called Reflected XSS, Stored and DOM-based, depending on the way malicious code is injected.

Reflected XSS

When a web page includes some or all of the input sent to the server as part of the request, an attacker can forge malicious URIs that will “reflect” the attack back to the user’s browser.

A possible scenario is the following:

  1. User visits a malicious Web page that embeds a link to the victim application;
  2. User clicks the link;
  3. The victim application returns a page containing the injected script;
  4. The script runs in the context of the victim application and leaks user’s sensitive data (circumventing SOP!)

Another typical scenario is when users are tricked into clicking on malicious links sent via mail.

Example: a basic attack

Typically, an attacker is interested in injecting a malicious Javascript code enclosed in <script></script> tags to steal the session cookie of an authenticated user. If the web application doesn’t properly validate the input data, as shown below, the attack is successful.

The following PHP snippet is a simple example of a vulnerable website. Notice that we set the X-XSS-Protection HTTP header to disable Cross-site scripting (XSS) filter built into most recent web browsers. We’ll see later (see Mitigations) why this is needed.



An attacker can inject arbitrary Javascript code by specifying the malicious payload as the value of either name or surname GET variables:

The resulting page will be:


Use haxor / xssleet to access the vulnerable website.

Example: stealing cookies

We can try to access session cookies via the following request (NOTE this will display all of your cookies, run this in an incognito browser window)

To steal the user’s sensitive cookies, the attacker may provide to the victim a link similar to the one below. That URL, indeed, leaks the cookie values to a web site under the attacker’s control. Sometimes suspicious links are hidden in various ways, e.g. by using a URL shortener service like tinyurl.

The victim’s browser executes the injected code and passes the list of cookies to a malicious website via the `cookie` variable. Notice that %2b is the urlencoding of character ‘+’. Notice that this bypasses the SOP policy since data are leaked from “inside” through the malicious script. This attack can be simulated by running a python HTTP server locally:

python3 -mhttp.server 8001
Serving HTTP on port 8001 ...

Then, we create an empty index.html and from an incognito window, we access the following URL:

On the server terminal we observe the leaked cookie: - - [11/Dec/2017 22:11:22] "GET /index.html?cookie=SESSID1%3D5fg6tdi39t8ag151117qkpuu51 HTTP/1.1" 200 -

Example: a stealthier attack

The previous example works but the user will notice the redirection to the attacker page. However, the attack can be easily made stealthier by performing the get request in the background. We exemplify using Javascript to load a (non existing) image:

The generated page will request the image attaching the cookies. Recall that SOP allows for embedding images cross-domain, so the request won’t be blocked:

Welcome, r1x  

Stored XSS

Stored attacks are those where the injected script is permanently stored on the target servers (e.g., as a message in a discussion board). The malicious payload is automatically executed by the user’s browser when the infected page is visited.

A typical scenario is the following:

  1. Attacker stores a malicious script in the victim application;
  2. User visit the victim page and executes the malicious script;
  3. As for reflected XSS, the script runs in the context of the victim application and leaks user’s sensitive data

Case study: Samy

The most famous Web “worm” samy was in fact a stored XSS.

DOM-based XSS

DOM-based XSS is similar to reflected XSS but in a DOM-based XSS the page is unmodified, i.e., the attack payload is not added in the page server-side. Instead, the modification occurs client-side, in the victim page due to existing honest scripts.

A possible scenario is the following:

  1. User visits a malicious Web page that embeds a link to the victim application;
  2. User clicks the link, containing malicious parameters;
  3. The victim application returns a page which does not yet contain the malicious payload;
  4. The host script processes the parameters and, as a side effect, incorporates the malicious code that is then executed in the context of the victim application

An example taken from OWASP follows:

Select your language:

The two following URLs show a honest and a malicious request:

Notice that this simple XSS would be blocked by XSS Auditor (see below).

Filter evasion

Instead of performing proper input sanitization, unwary developers often rely on filtering out potentially dangerous keywords like script. Unfortunately, XSS attacks may be performed without using the script tag, for example using inline Javascript:

Free iPhone

Other evasion techniques can be found in the OWASP XSS Filter Evasion Cheat Sheet.


As mentioned before, most reflected and stored XSS can be addressed by performing proper validation and by escaping html characters on the server-side.

Input validation

Input should never be trusted. It is important to strictly allow only what is expected: proper length, restricted characters, possibly matching a regexp. When the set of possible input is fixed it is very effective to use an explicit whitelist to validate the input.

Output validation

Before input is shown as output, it is important to:

  1. encode html characters so that they are not interpreted as html. In the case of PHP this can be done using either htmlspecialchars or htmlentities.
  2. avoid particularly dangerous insertion points (for example inserting input directly inside a script tag).

Detailed information can be found in the OWASP XSS Prevention Cheat Sheet.

There exist also libraries designed to cope with untrusted input supposed to contain HTML tags.


HttpOnly cookies

To reduce the risk of revealing cookies via injected scripts, it is possible to prevent any Javascript code to access cookies by setting the HttpOnly flag. If this flag is included in the HTTP response header from the webserver, the cookies cannot be accessed by client side scripts.

This can be easily done in PHP via various facilities. An example follows:



If we try to access the list of cookies via the following request, as we did before, we should notice that SESSID2 is missing since it cannot be accessed by the running scripts.

XSS Auditor

Mitigation strategies are also implemented by web browsers. Even if browsers are defenseless against stored XSS, since without a shared policy between the browser and the web application it is not possible to distinguish legitimate from malicious scripts, some effective countermeasures to attenuate the impact of reflected attacks have been developed. For instance, Google Chrome’s XSS auditor looks for code in the webpage that is found within the request. If a match is detected, the auditor raises an alert and prevents the injected script from being executed.

If we try to load the following resource using Google Chrome, by pressing F12 and displaying the “console” tab we should notice that the injection is detected:

The XSS Auditor refused to execute a script in '' because its source code was found within the request. The auditor was enabled as the server sent neither an 'X-XSS-Protection' nor 'Content-Security-Policy' header.

The source code of greet_normal.php is exactly the same as greet.php, with the only exception that XSS filters are not explicitly disabled using the X-XSS-Protection header:



The XSS Auditor is effective in many situations when dealing with reflected attacks, but it should be considered only as an additional protection layer. Indeed, our toy page greet_filter.php can be still exploited and the XSS Auditor bypassed by splitting the attack payload among the two variables:

Content Security Policy (CSP)

CSP is an extra security layer that mitigates some attacks, among which XSS. CSP is designed to be backward compatible: if either the browser or the server do not support it they will default to the standard SOP. CSP can be enabled through the Content-Security-Policy HTTP header.

CSP makes it possible to specify the domains that the browser should consider trusted sources of scripts. The browser will then only execute scripts loaded in source files received from the whitelisted domains, ignoring all other script (including inline scripts and event-handling HTML attributes). Even if probably too restrictive in many cases, it is also possible to globally disallow script execution.

The following example only allows content from own origin, and automatically forbids inline scripts:



XSS is prevented by this policy:

More information and examples can be found, e.g., at the mozilla CSP page.

More attack vectors

Cross-Site Scripting attacks are traditionally assimilated to injections of malicious Javascript code. There exist, however, alternative techniques, such as HTML or CSS injections, which allow to achieve goals comparable to the ones of traditional XSS attacks!