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.