Symmetric denial of service - TypeScript

Symmetric denial of service - TypeScript

Need

Prevention of repeated requests causing service outages

Context

  • Usage of TypeScript for statically typed JavaScript development
  • 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;

  // Simulate a long-running process
  for (let i = 0; i < 1000000000; i++) {
    // Do some heavy computation
  }

  res.send(`Search results for: ${query}`);
});

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

The vulnerability in the given code is a Symmetric Denial of Service (DoS) vulnerability. This vulnerability occurs because the server is performing a long-running computation in the request handler for the '/search' route.

In the code, when a GET request is made to the '/search' route, the server receives a query parameter 'q' from the request. However, instead of immediately processing the query and sending the search results, the code simulates a long-running process by executing a for loop that performs heavy computation.

This can lead to a denial of service situation because the server is unable to handle other requests while it is busy with the long-running computation. If multiple clients make simultaneous requests to the '/search' route, the server's resources can be exhausted, causing the server to become unresponsive or slow down significantly.

An attacker can exploit this vulnerability by repeatedly sending requests to the '/search' route, effectively overwhelming the server and denying access to legitimate users.

To mitigate this vulnerability, it is recommended to set a timeout for the long-running computation. This way, if the computation takes too long, the server can cancel it and respond with an appropriate error message or terminate the request.

Steps

  • Analyze the code to identify the heavy computation that is causing the long-running process.
  • Optimize the heavy computation to reduce the processing time.
  • Implement a timeout mechanism to limit the maximum processing time for the search request.
  • Return an appropriate response if the timeout is reached, indicating that the request took too long to process.
  • Update the code to handle the timeout and return a response accordingly.
  • Test the updated code to ensure it functions as expected and does not cause denial of service vulnerabilities.

Compliant code

        import express from 'express';

const app = express();

app.get('/search', (req, res) => {
  const query = req.query.q;

  // Set a timeout of 5 seconds
  const timeout = setTimeout(() => {
    res.status(503).send('Request timed out');
  }, 5000);

  // Simulate a long-running process
  for (let i = 0; i < 1000000000; i++) {
    // Do some heavy computation
  }

  clearTimeout(timeout);

  res.send(`Search results for: ${query}`);
});

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

The fixed code addresses the vulnerability by implementing a timeout mechanism for long-running requests.

In the code, we import the Express framework and create an instance of the application. We define a route for the '/search' endpoint using the HTTP GET method.

Within the route handler, we first extract the query parameter from the request object.

To prevent the server from becoming unresponsive due to a long-running process, we set a timeout of 5 seconds using the setTimeout function. If the request takes longer than 5 seconds to process, the timeout callback function will be executed.

Next, we simulate a long-running process by using a for loop that performs heavy computation. This is where the actual processing of the request takes place.

Before sending the response, we clear the timeout using the clearTimeout function. This ensures that if the request completes within the timeout period, the timeout callback function will not be executed.

Finally, we send the response with the search results.

By setting a timeout and clearing it before sending the response, we ensure that requests that take too long to process will be terminated and the server will not be overwhelmed.

References