Skip to main content

Server-side request forgery (SSRF)

Need

Prevention of unauthorized server-side requests to arbitrary domains

Context

  • Usage of TypeScript for statically typed JavaScript development
  • Usage of Express for building web applications and APIs
  • Usage of Axios for making HTTP requests

Description

Non compliant code

import express from 'express';
import axios from 'axios';

const app = express();

app.get('/fetch', async (req, res) => {
const { url } = req.query;
try {
const response = await axios.get(url);
res.send(response.data);
} catch (error) {
res.status(500).send('Error fetching data');
}
});

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

The vulnerability in this code is a Server-side Request Forgery (SSRF) vulnerability.

In the code, there is an endpoint /fetch that accepts a query parameter url. This parameter is directly used in the axios.get() function to make a request to the specified URL.

The problem is that the url parameter is not properly validated or sanitized before making the request. This means that an attacker can manipulate the url parameter to make the server send requests to arbitrary domains or IP addresses, potentially leading to unauthorized access to sensitive information or performing actions on behalf of the server.

An attacker can exploit this vulnerability by providing a malicious URL that points to internal resources or sensitive endpoints that should not be accessible to the public. For example, an attacker could provide a URL that fetches sensitive data from the server's internal network, or even make requests to the server itself, leading to further exploitation.

To fix this vulnerability, the code should implement proper input validation and sanitization for the url parameter. The system should restrict the endpoints that can be queried and ensure that only trusted and authorized URLs are allowed. Additionally, it is recommended to use a whitelist approach, where only specific URLs or domains are allowed, rather than trying to blacklist potentially malicious URLs.

Steps

  • Validate the 'url' parameter to ensure it is a valid and trusted URL.
  • Implement a whitelist of allowed domains that the server can make requests to.
  • Check if the 'url' parameter is in the whitelist before making the request.
  • If the 'url' parameter is not in the whitelist, return an error response instead of making the request.
  • Use a try-catch block to handle any errors that occur during the request.
  • Return an appropriate error response if an error occurs.
  • Consider implementing rate limiting or other security measures to prevent abuse of the SSRF protection.

Compliant code

import express from 'express';
import axios from 'axios';

const app = express();

// Step 2: Implement a whitelist of allowed domains
const allowedDomains = ['example.com', 'trusteddomain.com'];

app.get('/fetch', async (req, res) => {
const { url } = req.query;

// Step 1: Validate the 'url' parameter
if (!isValidUrl(url)) {
return res.status(400).send('Invalid URL');
}

// Step 3: Check if the 'url' parameter is in the whitelist
if (!isUrlAllowed(url)) {
return res.status(403).send('Forbidden');
}

try {
const response = await axios.get(url);
res.send(response.data);
} catch (error) {
// Step 6: Return an appropriate error response
res.status(500).send('Error fetching data');
}
});

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

// Helper function to validate URL
function isValidUrl(url: string): boolean {
// Implement your URL validation logic here
// Return true if the URL is valid, false otherwise
return true;
}

// Helper function to check if URL is allowed
function isUrlAllowed(url: string): boolean {
// Implement your whitelist logic here
// Return true if the URL is allowed, false otherwise
return allowedDomains.some(domain => url.includes(domain));
}

The fixed code is written in TypeScript and uses the Express framework for building the server. It addresses the vulnerability by implementing several measures to prevent server-side request forgery (SSRF).

  1. The code defines an array called allowedDomains which serves as a whitelist of trusted domains. This whitelist will be used to validate the URLs provided by users.

  2. The server exposes a GET endpoint at the /fetch route. When a request is made to this endpoint, it retrieves the url parameter from the query string.

  3. The code includes a helper function called isValidUrl which can be implemented to validate the URL provided by the user. In this example, the function returns true for simplicity, but you should implement your own URL validation logic to ensure the URL is valid.

  4. The code includes another helper function called isUrlAllowed which checks if the provided URL is in the whitelist of allowed domains. The function uses the allowedDomains array and checks if any of the domains are included in the URL. If the URL is allowed, the function returns true, otherwise, it returns false.

  5. Before making the request to the provided URL, the code validates the URL using the isValidUrl function. If the URL is invalid, it returns a 400 Bad Request response.

  6. The code then checks if the URL is allowed by calling the isUrlAllowed function. If the URL is not in the whitelist, it returns a 403 Forbidden response.

  7. If the URL is valid and allowed, the code uses the axios library to make a GET request to the provided URL. It awaits the response and sends the response data back to the client.

  8. If there is an error during the request, the code catches the error and returns a 500 Internal Server Error response.

By implementing these measures, the fixed code ensures that only valid and whitelisted URLs can be requested, mitigating the risk of server-side request forgery.

References