Skip to main content

Asymmetric denial of service - Content length

Need

Prevention of asymmetric denial of service attacks through proper validation and handling of the Content-Length field in HTTP requests.

Context

  • Usage of TypeScript for static typing and enhanced tooling in Node.js development
  • Usage of Express for building web applications and APIs

Description

Non compliant code

import express from 'express';

const app = express();

app.post('/vulnerable', (req, res) => {
const contentLength = parseInt(req.headers['content-length'] as string);
const data = req.body;

if (contentLength > 1000000) {
// Perform some action with the large amount of data
}

res.send('Success');
});

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

The vulnerability in this code is an asymmetric denial of service attack due to the handling of the Content-Length header.

In the code, the server receives a POST request to the '/vulnerable' endpoint. It retrieves the value of the Content-Length header from the request headers and parses it as an integer. The server then proceeds to process the request body.

The vulnerability lies in the if statement that checks if the contentLength is greater than 1000000 (1MB). If the Content-Length header value is set to a very high value, exceeding the threshold, the server will perform some action with the large amount of data.

This can be exploited by an attacker to exhaust server resources. The attacker can send a request with a spoofed Content-Length header containing a very high value, causing the server to expect a large amount of data. However, the attacker only needs to send a small piece of data before the connection termination timer expires. This keeps the connection active and can lead to the exhaustion of server resources.

The impact of this vulnerability includes exhausting all available server resources, using techniques to exhaust all available server resources, and potentially exhausting the victim's network and hardware resources when requesting large amounts of data.

To mitigate this vulnerability, it is recommended to set a maximum reasonable length for the header and message body. Additionally, defining a minimum incoming data rate and dropping slower requests can help prevent this type of attack. It is also important to set an absolute connection timeout to limit the duration of active connections.

Steps

  • Validate the content length header to ensure it is within a reasonable range.
  • Implement rate limiting to drop incoming requests that are slower than a defined minimum data rate.
  • Set an absolute connection timeout to terminate connections that are kept dangerously active.
  • Consider using a middleware or library that provides additional security features for handling incoming requests.

Compliant code

import express from 'express';

const app = express();

const MAX_CONTENT_LENGTH = 1000000;
const MIN_DATA_RATE = 100; // Minimum data rate in bytes per second
const CONNECTION_TIMEOUT = 5000; // Connection timeout in milliseconds

app.post('/vulnerable', (req, res) => {
const contentLength = parseInt(req.headers['content-length'] as string);
const data = req.body;

if (contentLength > MAX_CONTENT_LENGTH) {
// Perform some action with the large amount of data
}

res.send('Success');
});

// Middleware to validate content length
app.use((req, res, next) => {
const contentLength = parseInt(req.headers['content-length'] as string);

if (contentLength > MAX_CONTENT_LENGTH) {
return res.status(413).send('Payload Too Large');
}

next();
});

// Middleware for rate limiting
app.use((req, res, next) => {
const start = Date.now();

res.on('finish', () => {
const end = Date.now();
const duration = end - start;
const dataRate = req.headers['content-length'] ? contentLength / (duration / 1000) : 0;

if (dataRate < MIN_DATA_RATE) {
// Drop the request if the data rate is slower than the defined minimum
return;
}
});

next();
});

// Middleware for connection timeout
app.use((req, res, next) => {
req.socket.setTimeout(CONNECTION_TIMEOUT);
next();
});

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

The fixed code addresses the vulnerability by implementing several measures to ensure the safety of the application.

  1. Maximum Content Length Validation:

    • The code defines a constant MAX_CONTENT_LENGTH which represents the maximum allowed content length for incoming requests.
    • When handling a POST request, the code checks the Content-Length header of the request to determine the size of the transmitted data.
    • If the content length exceeds the maximum allowed value, the code can perform some action to handle the large amount of data.
  2. Content Length Validation Middleware:

    • The code includes a middleware function that validates the Content-Length header of every incoming request.
    • If the content length exceeds the maximum allowed value, the middleware returns a 413 Payload Too Large response, indicating that the request payload is too large.
  3. Rate Limiting Middleware:

    • The code includes a middleware function that calculates the data rate of incoming requests.
    • It measures the time taken to process the request and calculates the data rate by dividing the content length by the duration.
    • If the data rate is slower than the defined minimum (MIN_DATA_RATE), the middleware drops the request, preventing slow data rate attacks.
  4. Connection Timeout Middleware:

    • The code includes a middleware function that sets a connection timeout for each incoming request.
    • It sets the timeout value to CONNECTION_TIMEOUT milliseconds, ensuring that the connection is terminated if it exceeds the specified time limit.

By implementing these measures, the fixed code mitigates the vulnerability by:

  • Limiting the maximum allowed content length to prevent excessive resource consumption.
  • Validating the content length and rejecting requests with payloads that exceed the limit.
  • Enforcing a minimum data rate to prevent slow data rate attacks.
  • Setting a connection timeout to ensure connections are terminated within a specified time limit.

References