Performance and Security Testing
Performance and Security Testing
Performance testing in a security context identifies denial-of-service vulnerabilities and resource exhaustion attacks. APIs must handle high loads gracefully without exposing sensitive information or becoming unavailable to legitimate users.
// Java JMeter-based security performance testing
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
import org.apache.jmeter.threads.ThreadGroup;
import java.util.concurrent.*;
public class SecurityPerformanceTests {
public class RateLimitingTest {
private final String apiEndpoint;
private final String apiKey;
private final ExecutorService executor;
public void testRateLimitEnforcement() throws Exception {
int numberOfThreads = 100;
int requestsPerThread = 100;
CountDownLatch latch = new CountDownLatch(numberOfThreads);
List<Future<TestResult>> futures = new ArrayList<>();
// Launch concurrent requests
for (int i = 0; i < numberOfThreads; i++) {
futures.add(executor.submit(() -> {
TestResult result = new TestResult();
for (int j = 0; j < requestsPerThread; j++) {
Response response = makeRequest();
result.totalRequests++;
if (response.getStatus() == 200) {
result.successfulRequests++;
} else if (response.getStatus() == 429) {
result.rateLimitedRequests++;
// Verify rate limit headers
assertTrue(response.hasHeader("X-RateLimit-Limit"));
assertTrue(response.hasHeader("X-RateLimit-Remaining"));
assertTrue(response.hasHeader("Retry-After"));
}
}
latch.countDown();
return result;
}));
}
// Wait for completion
latch.await(60, TimeUnit.SECONDS);
// Analyze results
TestResult aggregated = aggregateResults(futures);
// Verify rate limiting is working
assertTrue(aggregated.rateLimitedRequests > 0,
"No rate limiting observed");
// Verify rate limit is reasonable
double successRate = (double) aggregated.successfulRequests /
aggregated.totalRequests;
assertTrue(successRate < 0.9,
"Rate limit might be too permissive");
assertTrue(successRate > 0.1,
"Rate limit might be too restrictive");
}
}
public class ResourceExhaustionTest {
public void testLargePayloadHandling() {
// Test increasingly large payloads
int[] payloadSizes = {1_000, 10_000, 100_000, 1_000_000, 10_000_000};
for (int size : payloadSizes) {
String largePayload = generatePayload(size);
long startTime = System.currentTimeMillis();
Response response = postData(apiEndpoint, largePayload);
long duration = System.currentTimeMillis() - startTime;
// API should reject overly large payloads quickly
if (size > 1_000_000) {
assertEquals(413, response.getStatus(),
"Large payload not rejected");
assertTrue(duration < 1000,
"Took too long to reject large payload");
}
// Verify no timeout or memory errors
assertNotEquals(503, response.getStatus());
assertFalse(response.getBody().contains("OutOfMemory"));
}
}
public void testSlowlorisAttackProtection() throws Exception {
int connections = 100;
List<Socket> sockets = new ArrayList<>();
try {
// Open many connections
for (int i = 0; i < connections; i++) {
Socket socket = new Socket("api.example.com", 443);
sockets.add(socket);
// Send partial HTTP request
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.print("POST /api/endpoint HTTP/1.1\r\n");
out.print("Host: api.example.com\r\n");
out.flush();
// Don't complete the request
Thread.sleep(100);
}
// Try legitimate request
long startTime = System.currentTimeMillis();
Response response = makeNormalRequest();
long duration = System.currentTimeMillis() - startTime;
// Legitimate requests should still work
assertEquals(200, response.getStatus());
assertTrue(duration < 5000,
"API might be vulnerable to Slowloris");
} finally {
// Clean up connections
for (Socket socket : sockets) {
socket.close();
}
}
}
public void testComputationalComplexityAttack() {
// Test with nested/recursive data structures
String nestedJson = generateNestedJson(100);
long startTime = System.currentTimeMillis();
Response response = postJson(apiEndpoint, nestedJson);
long duration = System.currentTimeMillis() - startTime;
// Should handle nested data efficiently
assertTrue(duration < 5000,
"Processing deeply nested data too slow");
// Test with regex catastrophic backtracking
String maliciousRegex = "a" + "a".repeat(100) + "b";
Response regexResponse = postData(
"/api/search",
"{\"pattern\": \"(a+)+b\", \"text\": \"" + maliciousRegex + "\"}"
);
// Should timeout or reject, not hang
assertTrue(regexResponse.getStatus() == 400 ||
regexResponse.getStatus() == 408);
}
}
}
Comprehensive API security testing requires continuous effort throughout the development lifecycle. The next chapter explores ongoing security monitoring and logging practices that complement testing efforts.