Distributed Denial Of Service in Grpc APIs
How Distributed Denial Of Service Manifests in Grpc
Distributed Denial of Service (DDoS) attacks targeting gRPC services exploit the protocol's unique characteristics to overwhelm servers with high-volume traffic. Unlike traditional HTTP-based APIs, gRPC uses HTTP/2 under the hood, which introduces specific attack vectors that attackers can leverage.
The most common gRPC DDoS attack pattern involves overwhelming the connection pool. gRPC maintains HTTP/2 streams where multiple requests can be multiplexed over a single TCP connection. An attacker can exploit this by establishing numerous connections and flooding them with requests, causing the server to exhaust its connection limits. Each gRPC request creates a new stream, and the server must allocate resources to manage these streams, making it vulnerable to stream exhaustion attacks.
Another critical attack vector is the use of large message payloads. gRPC supports streaming of large binary data through its Protocol Buffers serialization. Attackers can send massive streams of data, forcing the server to buffer and process enormous amounts of information, consuming memory and CPU resources. This becomes particularly problematic with bidirectional streaming RPCs where both client and server can send messages continuously.
Timing-based attacks also pose significant risks. gRPC's flow control mechanisms, while designed for efficiency, can be manipulated. Attackers can send requests with deliberately slow responses, keeping connections open and consuming server resources. This 'slowloris' style attack is amplified in gRPC because the protocol maintains persistent connections and expects bidirectional communication.
Service discovery and load balancing features in gRPC can be exploited for amplification attacks. If an attacker discovers the service registry, they can target specific services or nodes, overwhelming particular components of a distributed system. The metadata handling in gRPC also presents opportunities - excessive or malformed metadata in requests can cause parsing overhead and memory consumption.
Rate limiting becomes more complex with gRPC because traditional rate limiters designed for REST APIs don't account for gRPC's multiplexing and streaming capabilities. An attacker can rotate through different methods and services, bypassing simple rate limits that track requests per endpoint rather than per connection or per user.
Code examples of vulnerable patterns include:
services:
Greeter:
rpc:
SayHello:
request:
HelloRequest:
name: string
response:
HelloReply:
message: string
This simple service definition becomes vulnerable when implemented without proper resource limits. An attacker can repeatedly call SayHello with varying payloads, potentially causing memory exhaustion if the implementation doesn't validate input sizes or enforce timeouts.
Grpc-Specific Detection
Detecting DDoS vulnerabilities in gRPC services requires specialized tools that understand the protocol's unique characteristics. Traditional HTTP-based security scanners often miss gRPC-specific attack patterns because they don't parse Protocol Buffers or understand HTTP/2 stream management.
middleBrick's gRPC scanning capabilities include protocol-aware analysis that examines the service definition files and runtime behavior. The scanner parses .proto files to understand the RPC methods, message structures, and streaming capabilities before testing. This allows it to craft targeted attacks that probe for resource exhaustion vulnerabilities specific to each method type.
The detection process begins with service discovery. middleBrick can identify gRPC endpoints by examining TLS certificates, ALPN negotiation, and HTTP/2 connection patterns. Once discovered, it analyzes the service structure by attempting to retrieve the gRPC reflection service if enabled, which provides a complete map of available methods and their signatures.
For each RPC method, middleBrick tests multiple attack scenarios:
- Connection flood attacks to test connection pool limits
- Payload amplification by sending oversized messages
- Streaming exhaustion by creating long-lived bidirectional streams
- Metadata abuse by sending excessive or malformed metadata headers
- Rate limit bypass attempts using different method combinations
The scanner monitors server responses for indicators of resource exhaustion, including increased response times, HTTP/2 connection resets, and error codes specific to gRPC like RESOURCE_EXHAUSTED or UNAVAILABLE. It also tracks connection lifecycle events to identify patterns consistent with connection pool attacks.
middleBrick's analysis includes examining the service's configuration for missing protections. This includes checking for absent rate limiting configurations, missing timeout settings, and lack of request size limits. The scanner can detect when services rely on default gRPC settings that may be too permissive for production environments.
Real-time monitoring during scans captures metrics like connection establishment times, stream creation rates, and memory usage patterns. This data helps identify services that might handle normal traffic well but become unstable under load. The scanner can simulate various traffic patterns to test how the service behaves under different stress conditions.
Code-level analysis examines the implementation for common anti-patterns that exacerbate DDoS vulnerabilities. This includes checking for blocking operations in RPC handlers, missing context cancellation handling, and unbounded resource allocation. The scanner can identify methods that might work correctly in isolation but become problematic when called concurrently at scale.
Grpc-Specific Remediation
Securing gRPC services against DDoS attacks requires a multi-layered approach that addresses the protocol's specific characteristics. The first layer of defense involves proper configuration of gRPC server settings to limit resource consumption.
Connection management is critical. Implement maximum connection limits and configure keep-alive policies to prevent connection hoarding:
import { Server, ServerCredentials } from '@grpc/grpc-js';
const server = new Server({
'grpc.max_connection_age_ms': 300000, // 5 minutes
'grpc.keepalive_time_ms': 60000, // 1 minute
'grpc.max_connection_idle_ms': 300000, // 5 minutes
'grpc.max_concurrent_streams': 100
});
Rate limiting in gRPC requires understanding the protocol's multiplexing. Implement per-connection and per-user rate limits rather than simple request counting:
import { RateLimiterMemory } from 'rate-limiter-flexible';
const rateLimiter = new RateLimiterMemory({
keyGenerator: (req) => req.metadata.get('authorization') || req.connection.remoteAddress,
points: 100, // 100 requests
duration: 60 // per minute
});
server.addMethod('SayHello', async (call, callback) => {
try {
await rateLimiter.consume(call.metadata.get('authorization'));
// process request
} catch (rejRes) {
callback({
code: grpc.status.RESOURCE_EXHAUSTED,
message: 'Rate limit exceeded'
});
}
});
Request size limiting prevents payload amplification attacks:
const server = new Server({
'grpc.max_receive_message_length': 4 * 1024 * 1024 // 4MB limit
});
Timeouts and context cancellation are essential for preventing slowloris attacks:
server.addMethod('LongRunningTask', async (call) => {
const deadline = Date.now() + 30000; // 30 second timeout
while (Date.now() < deadline) {
if (call.cancelled) {
return; // cancelled by client or timeout
}
// process work
await new Promise(resolve => setTimeout(resolve, 100));
}
});
Streaming methods require special attention to prevent resource exhaustion:
server.addMethod('BidirectionalStream', async (call) => {
let messageCount = 0;
call.on('data', (message) => {
if (++messageCount > 1000) { // limit to 1000 messages
call.destroy();
return;
}
// process message
});
call.on('end', () => {
call.end();
});
});
Metadata validation prevents abuse of the metadata system:
server.addMethod('SecureMethod', async (call, callback) => {
const metadata = call.metadata;
if (metadata.length > 50) { // limit number of metadata entries
callback({
code: grpc.status.INVALID_ARGUMENT,
message: 'Excessive metadata'
});
return;
}
// validate specific metadata values
const auth = metadata.get('authorization');
if (!auth || !auth.startsWith('Bearer ')) {
callback({
code: grpc.status.UNAUTHENTICATED,
message: 'Missing or invalid authorization'
});
return;
}
});
Load balancing and service discovery should include health checking to prevent traffic from being routed to unhealthy nodes:
import { Health, health } from '@grpc/grpc-js';
const healthService = new Health();
healthService.setServiceHealth('myservice.Greeter', health.HealthCheckResponse.ServingStatus.SERVING);
server.addService(healthService);
Monitoring and alerting systems should track gRPC-specific metrics like active streams per connection, message sizes, and error rates by method. This allows early detection of potential DDoS patterns before they impact service availability.