Prevention of service outages caused by repeated resource-intensive requests
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
void main() {
var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
.addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080);
}
shelf.Response _echoRequest(shelf.Request request) {
// This is a resource-intensive operation
for (int i = 0; i < 100000000; i++) {
print(i);
}
return shelf.Response.ok('Request processed.');
}
The above Dart code represents a simple server application using the
shelf
framework. The application listens to requests on port 8080 and processes them using the
_echoRequest
function.
The vulnerability lies in the
_echoRequest
function where a resource-intensive operation is performed. In this case, the function runs a loop for 100,000,000 iterations and prints each iteration to the console. This operation consumes a lot of system resources and takes a considerable amount of time to complete.
If an attacker repeatedly sends requests to the server, each request will trigger this resource-intensive operation. As a result, the server will be overwhelmed with the processing of these requests and will become unresponsive. This is known as a Symmetric Denial of Service (DoS) vulnerability.
import 'dart:async';
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_rate_limiter/shelf_rate_limiter.dart';
void main() {
var handler = const shelf.Pipeline()
.addMiddleware(shelf.logRequests())
.addMiddleware(rateLimiterMiddleware(maxOperationsPerPeriod: 5, period: Duration(seconds: 10)))
.addHandler(_echoRequest);
io.serve(handler, 'localhost', 8080);
}
Future _echoRequest(shelf.Request request) async {
// This is a resource-intensive operation
await Future.delayed(Duration(seconds: 2)); // Simulate delay
return shelf.Response.ok('Request processed.');
}
The updated code includes several changes to mitigate the risk of a symmetric denial of service attack.
1. Rate Limiting: We've added a rate limiter middleware to the server pipeline. This middleware limits the number of requests a client can make in a given period. In this case, a client can make a maximum of 5 requests every 10 seconds. This prevents a single client from overwhelming the server with too many requests.
2. Asynchronous Programming: The
_echoRequest
function has been updated to be an asynchronous function. This means that it returns a
Future
, which allows the server to handle other requests while it's waiting for the resource-intensive operation to complete. In this case, we've simulated a delay with
Future.delayed
, but in a real-world scenario, this could be a database query, file operation, or other time-consuming task.
3. Optimization: The resource-intensive operation (a loop that prints numbers from 0 to 100000000) has been removed. In a real-world scenario, you would need to identify and optimize any operations that consume a lot of resources or take a long time to complete.
4. Monitoring and Logging: The server uses the
logRequests
middleware to log all incoming requests. This can help you detect unusual activity or repeated requests from a single client, which could indicate a potential denial of service attack.