Skip to main content

DOM-Based cross-site scripting (XSS)

Need

Prevention of cross-site scripting attacks through proper input validation and output encoding

Context

  • Usage of TypeScript for type-checking and compiling JavaScript code
  • Usage of Express for building web applications and APIs

Description

Non compliant code

import express from 'express';

const app = express();

app.get('/search', (req, res) => {
const query = req.query.q;
const response = `<h1>Search Results for: ${query}</h1>`;
res.send(response);
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The vulnerability in the given code is a DOM-Based cross-site scripting (XSS) vulnerability. This vulnerability occurs because the code directly uses the user-provided input (req.query.q) to generate dynamic web content without properly validating or sanitizing it.

In the vulnerable code, the app.get('/search') route handler receives a query parameter q from the client's request using req.query.q. It then directly uses this input to construct an HTML response string (response) which includes the user-provided input without any validation or sanitization.

An attacker can exploit this vulnerability by injecting malicious scripts into the q parameter. When the server generates the response and sends it back to the client, the injected script will be executed in the context of the user's browser, potentially leading to unauthorized actions, data theft, or other malicious activities.

To mitigate this vulnerability, it is recommended to perform proper input validation and sanitization on both the server-side and client-side. This can be achieved by implementing measures such as input validation, output encoding, and using secure frameworks or libraries that handle input sanitization automatically.

Steps

  • Perform server-side input validation to ensure that the query parameter is safe to use.
  • Escape the query parameter before including it in the response to prevent any potential script injection.
  • Implement client-side input validation to further protect against script injection attacks.

Compliant code

import express from 'express';
import { escape } from 'html-escaper';

const app = express();

app.get('/search', (req, res) => {
const query = escape(req.query.q);
const response = `<h1>Search Results for: ${query}</h1>`;
res.send(response);
});

app.listen(3000, () => {
console.log('Server is running on port 3000');
});

The fixed code addresses the vulnerability by performing input data validation on both the server-side and client-side.

In the code, the escape function from the html-escaper library is imported. This function helps to sanitize the input data by escaping any special characters that could potentially be used for script injection.

The code defines an Express application and sets up a route for the /search endpoint. When a GET request is made to this endpoint, the code retrieves the query parameter (req.query.q) from the request object and passes it through the escape function to sanitize it.

The sanitized query is then used to generate a response string that includes the search query in an HTML heading element. This ensures that any potentially malicious scripts are treated as plain text and not executed.

Finally, the response is sent back to the client using the res.send method.

By performing input data validation and sanitization, the fixed code prevents the injection of malicious scripts into the dynamically generated web content, effectively mitigating the risk of DOM-based cross-site scripting (XSS) attacks.

References