In cross-site scripting (XSS) attacks, malicious code on a webpage can jeopardize sensitive user information. Learn how to prevent this vulnerability in your JavaScript app.
Cross-site scripting (XSS) is a type of security vulnerability that allows an attacker to inject malicious code into a webpage viewed by other users. The attacker can use this vulnerability to steal sensitive information from the user, such as login credentials or credit card numbers.
There are three types of cross-site scripting attacks:
Reflected XSS: In this type of attack, the attacker injects malicious code into a webpage that is immediately returned to the user’s browser. This can happen when the user submits a form that is vulnerable to XSS. Let’s say there is a web application that allows users to search for products on an ecommerce site. The search functionality is implemented using a URL parameter, like so:
https://www.example.com/search?q=<search_term>
When a user enters a search term and submits the form, the search term is included in the URL as a parameter. The server then processes the search request and returns the search results in the response.
Now, imagine an attacker creates a malicious link that includes a script tag in the search term parameter:
https://www.example.com/search?q=<script>alert('XSS attack!')</script>
If a user clicks on this link, their browser will send a request to the server with the malicious search term in the URL parameter. The server will then include the search term in the response, which will contain the script tag. The user’s browser will interpret the script tag and execute the JavaScript code, which in this case will display an alert box with the message “XSS attack!”
Stored XSS: In this type of attack, the attacker injects malicious code into a webpage that is stored on the server and served to all users who view that page.
Let’s say there is a social media website that allows users to post comments on other users’ profiles. The comments are stored in a database and displayed on the profile page. The website does not properly sanitize user input and allows users to post comments containing HTML and JavaScript code.
An attacker creates a malicious comment that includes a script tag, like so:
<script>alert('Stored XSS attack!')</script>
When the attacker posts this comment on a victim’s profile, it is stored in the database. The next time the victim views their profile, the malicious comment is displayed on the page and the victim’s browser executes the script tag, displaying an alert box with the message “Stored XSS attack!”
Unlike a reflected XSS attack, the malicious code is not included in the URL parameter and is not dependent on the victim clicking on a malicious link. Instead, the code is stored on the server and is executed every time the victim views their profile, potentially allowing the attacker to steal sensitive information or perform unauthorized actions on behalf of the victim without their knowledge.
DOM-based XSS: In this type of attack, the attacker injects malicious code into a webpage that is executed by the user’s browser, usually through JavaScript.Let’s say there is a webpage that contains a search box, and the search results are displayed dynamically using JavaScript. The JavaScript code that handles the search functionality contains a vulnerability that allows an attacker to inject malicious code into the DOM.
For example, consider the following search box:
<input type="text" id="searchBox">
<button onclick="search()">Search</button>
<div id="results"></div>
And the following JavaScript code that handles the search functionality:
function search() {
var query = document.getElementById("searchBox").value;
var resultsDiv=cument.getElementById("results");
resultsDiv.innerHTML = "Search results for:" + query;
}
The vulnerability here is that the search term entered by the user is directly added to the DOM using the innerHTML property, without being properly sanitized or encoded.
An attacker can exploit this vulnerability by entering a search term that contains malicious JavaScript code, such as:
<script>alert('DOM-based XSS attack!')</script>
If the user enters this search term and clicks the search button, the attacker’s code will be executed by the victim’s browser, displaying an alert box with the message “DOM-based XSS attack!”
The first step in preventing XSS is to sanitize all user input before it is displayed on the webpage. This can be done using a variety of techniques, including input validation and output encoding.
Input validation involves checking user input to check that it meets certain criteria. For example, if you are expecting a user to enter an email address, you can check that the input contains the “@” symbol and a domain name. If the input does not meet these criteria, it should be rejected.
Output encoding involves encoding user input so that it is not interpreted as HTML or JavaScript code. This can be done using a variety of techniques, including HTML escaping and JavaScript encoding.
Here is an example of HTML escaping in JavaScript:
function escapeHTML(str) {
return str.replace(/[&<>"'\/]/g, function (char) (
switch (char) {
case '&':
return '&';
case '<':
return '<';
case '>':
return '>';
case '"':
return '"';
case '\':
return ''';
case '/':
return '/';
default:
return char;
}
});
}
This function takes a string as input and replaces any characters that have special meaning in HTML (such as <
and >
) with their corresponding HTML entities.
A Content Security Policy (CSP) is a security feature that allows you to specify which sources of content are allowed to be loaded on your webpage. This can help prevent XSS attacks by preventing malicious code from being loaded from untrusted sources.
Here is an example of how to use CSP in JavaScript:
// Set the Content Security Policy header
app.use(function (req, res, next) {
res.setHeader('Content-Security-Policy, "default-src 'self' ");
next();
});
// Load scripts only from trusted sources
<script src="[https://trusted-site.com/script.js](https://trusted-site.com/script.js)"></script>
This code sets the Content-Security-Policy header to only allow content from the same domain as the webpage. It also specifies that scripts can only be loaded from a trusted source.
Another example is a CSP with an HTTP header:
Content-Security-Policy: default-src 'self'
This header specifies that only content from the same origin as the page is allowed to be loaded.
The most effective way to prevent cross-site scripting is to validate all user input before it is displayed on a page. This can be achieved using regular expressions or other validation techniques to check that the input only contains allowed characters and is not malicious. For example, to validate a user’s email address, you can use the following regular expression:
function validateEmail (email) {
const pattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
return pattern.test(email);
}
This function uses a regular expression to validate that the email address contains only valid characters and follows the standard email address format.
Another way to prevent cross-site scripting is to encode all user input before it is displayed on a page. Encoding involves converting special characters into their corresponding HTML entities, which are not executable by a web browser. For example, to encode a user’s input, you can use the following function:
function encodeInput (input) {
const encoded = document.createElement('div');
encoded.innerText = input;
return encoded.innerHTML;
}
This function creates a new div element and sets its inner text to the user input. The inner HTML of the div element is then returned, which contains the encoded input.
HTTP-only cookies are cookies that can only be accessed by the web server, not by client-side JavaScript code. This prevents attackers from stealing sensitive information, such as login credentials, by injecting malicious code into your webpages.
Here is an example of using HTTP-only cookies in JavaScript:
document.cookie = "sessionID=12345; HttpOnly";
This code sets a cookie named “sessionID” with a value of “12345” and sets the HttpOnly flag to true. This makes it so that the cookie can only be accessed by the web server and not by client-side JavaScript code.
In conclusion, cross-site scripting (XSS) is a serious security vulnerability that allows attackers to inject malicious code into webpages that can steal sensitive information or perform unauthorized actions on behalf of the victim. There are three types of XSS attacks: Reflected XSS, Stored XSS and DOM-based XSS.
To prevent XSS attacks, it is crucial to sanitize all user input before it is displayed on a webpage using techniques like input validation and output encoding. It is also essential to use a Content Security Policy (CSP) and input validation to help establish that only trusted sources and allowed characters are loaded on a webpage. By implementing these measures, developers can significantly reduce the risk of XSS attacks and protect their users’ sensitive information.
Nduka John is a cyberthreat intelligence analyst and a technical writer for SaaS brands. His publications give clear insight on how to stay proactive towards cyberthreats through healthy cybersecurity practices.