Skip to content

Commit

Permalink
Merge pull request #20 from carlspring/missing-input-validation
Browse files Browse the repository at this point in the history
Add example for blocking the event loop
  • Loading branch information
carlspring authored Jul 7, 2023
2 parents 3c941ed + 368dee7 commit 0ada33a
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.carlspring.security.vertx.http;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

/**
* In this example, the Vert.x HTTP server expects a user-supplied input parameter called "data" via a query parameter
* or request body. The input is then converted into a {@link JsonArray} and each element of the {@link JsonArray} is
* iterated and processed sequentially. The processing involves some complex computations, which might work well with
* small data, but not for large {@link JsonArray}s.
*
* An attacker can exploit this vulnerability by injecting a large {@link JsonArray} as the input parameter,
* overwhelming the server's processing capabilities. For example, if the attacker sends a request with a large
* {@link JsonArray} containing thousands or millions of elements, the server will iterate through each element,
* causing a significant delay. As the workload is executed directly on the eventloop, such an attack would violate the
* <a href="https://vertx.io/docs/vertx-core/java/#golden_rule">Golden Rule of Vert.x<a/>, and block other tasks.
*
* To mitigate this vulnerability, you should consider implementing appropriate input validation and limiting the
* amount of data processed in a single request. You can also offload the processing of the {@link JsonArray} to a worker
* thread or divide the processing into smaller batches to avoid blocking the event loop and handle large inputs more
* efficiently.
*/
public class InsecureEventLoopWorkloads extends AbstractVerticle {

@Override
public void start() {

vertx.createHttpServer().requestHandler(req -> {
String input = req.getParam("data"); // User-supplied input

// Transform the input without validation
JsonArray jsonArray = new JsonArray(input);

// Process the input
JsonObject transformedObject = new JsonObject();
for (Object jsonObject : jsonArray) {
transformedObject = performExpensiveProcessing((JsonObject) jsonObject);
}

// Return the transform input
req.response().end(transformedObject.toBuffer());
}).listen(8080);
}

private JsonObject performExpensiveProcessing(JsonObject jsonObject) {
// Expensive transformation logic here; simulated with a sleep.
// This can include complex computations, database queries, network calls, etc.
try {
Thread.sleep(100); // Simulating processing time
} catch (InterruptedException e) {
e.printStackTrace();
}
return jsonObject;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.carlspring.security.vertx.http;

import io.vertx.core.AbstractVerticle;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;

/**
* In this updated code, the executeBlocking method is used to offload the processing of each JsonObject to a worker
* thread, ensuring that it doesn't block the event loop. The expensive transformation logic is still performed, but
* now it's executed on a worker.
*/
public class SecureEventLoopWorkloads extends AbstractVerticle {

private static final int MAX_INPUT_SIZE = 1_000; // Maximum allowed input size

@Override
public void start() {

vertx.createHttpServer().requestHandler(req -> {
String input = req.getParam("data"); // User-supplied input

// Execute processing on a worker
vertx.executeBlocking(promise -> {
// Transform the input without validation
JsonArray jsonArray = new JsonArray(input);

// Process the input
JsonObject transformedObject = new JsonObject();
for (Object jsonObject : jsonArray) {
transformedObject = performExpensiveProcessing((JsonObject) jsonObject);
}

promise.complete(transformedObject);
}, result -> {
if (result.succeeded()) {
req.response().end(result.result().toString());
} else {
req.response().setStatusCode(500).end("Internal Server Error");
}
});
}).listen(8080);
}

private JsonObject performExpensiveProcessing(JsonObject jsonObject) {
// Expensive transformation logic here; simulated with a sleep.
// This can include complex computations, database queries, network calls, etc.
try {
Thread.sleep(100); // Simulating processing time
} catch (InterruptedException e) {
e.printStackTrace();
}
return jsonObject;
}
}

0 comments on commit 0ada33a

Please sign in to comment.