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.