Rate Limiting and DDOS Protection

Defending Web Applications from Excessive Traffic and Attacks

Introduction to Service Availability

As we've explored various security topics, we've focused primarily on protecting data and preventing unauthorized access. Today, we shift our focus to another critical aspect of security: ensuring your application remains available and responsive, even under heavy load or malicious traffic conditions.

Service availability is a crucial part of the CIA triad (Confidentiality, Integrity, Availability) that forms the foundation of information security. No matter how secure your data is, if users can't access your service, your application has failed in its purpose.

flowchart TD A[Web Application Security] A --> B[Confidentiality] A --> C[Integrity] A --> D[Availability] B --> B1[Data Privacy] B --> B2[Access Controls] C --> C1[Data Accuracy] C --> C2[Data Completeness] D --> D1[Rate Limiting] D --> D2[DDOS Protection] D --> D3[Load Balancing] D --> D4[Scaling Strategies] style D fill:#f8f9fa,stroke:#495057 style D1 fill:#e9ecef,stroke:#495057 style D2 fill:#e9ecef,stroke:#495057

The Business Impact of Downtime

Service outages have serious business implications:

Real-World Example: Amazon's 2018 Prime Day Outage

During Amazon's 2018 Prime Day, the site experienced intermittent outages for several hours. Analysts estimated the company lost between $72 million and $99 million in sales during this period. The outage was reportedly caused by insufficient server capacity to handle the traffic spike, not by a DDoS attack. This illustrates how even routine high traffic can cause availability issues when proper traffic management isn't in place.

Understanding Common Traffic Challenges

Before diving into solutions, it's important to understand the different types of traffic challenges your application might face:

Normal Traffic Spikes

Not all high-traffic situations are malicious. Many legitimate scenarios can cause traffic surges:

API Abuse and Scraping

Some high-volume traffic comes from automated systems that may not be malicious but can still overwhelm your services:

Intentional Denial of Service

Sometimes traffic is deliberately malicious, designed to overwhelm your services:

Types of DDoS Attacks Web Server Layer 3/4 Attacks 🖥️ 🖥️ 🖥️ SYN Flood UDP Flood ICMP Flood Layer 7 Attacks 🖥️ 🖥️ 🖥️ HTTP Flood Slow Loris Cache Busting Resource Exhaustion 🖥️ 🖥️ Search Query Attacks Recursive Queries

Rate Limiting: Concepts and Strategies

Rate limiting is the practice of controlling the amount of requests a user or client can make to your API or application within a given time period. It's a crucial tool for ensuring fair resource usage and protecting against various traffic-related issues.

Key Concepts in Rate Limiting

Rate Limiting Algorithms

Several algorithms can be used to implement rate limiting, each with different characteristics:

graph TD A[Rate Limiting Algorithms] --> B[Fixed Window] A --> C[Sliding Window] A --> D[Leaky Bucket] A --> E[Token Bucket] B --> B1[Simple to implement] B --> B2[Potential burst at window edges] C --> C1[Smoother rate control] C --> C2[More memory intensive] D --> D1[Processing at fixed rate] D --> D2[Excess requests dropped] E --> E1[Allows controlled bursts] E --> E2[Regular token replenishment]

Fixed Window Counter

The simplest approach divides time into fixed intervals (e.g., 1-minute windows) and counts requests in each window.


// Conceptual implementation of Fixed Window algorithm
class FixedWindowRateLimiter {
    constructor(maxRequests, windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
        this.clients = new Map();
    }
    
    isAllowed(clientId) {
        const now = Date.now();
        const windowStart = Math.floor(now / this.windowMs) * this.windowMs;
        
        if (!this.clients.has(clientId) || this.clients.get(clientId).windowStart !== windowStart) {
            // New window for this client
            this.clients.set(clientId, {
                windowStart,
                count: 1
            });
            return true;
        }
        
        const client = this.clients.get(clientId);
        if (client.count < this.maxRequests) {
            // Increment counter
            client.count++;
            return true;
        }
        
        return false; // Rate limit exceeded
    }
}
                

Drawback: This approach can allow twice the rate limit across a boundary. For example, with a limit of 100 requests per minute, a client could make 100 requests at 10:59:59 and another 100 requests at 11:00:00, getting 200 requests in a 2-second period.

Sliding Window Log

This algorithm stores a timestamp for each request and counts requests within the sliding time window.


// Conceptual implementation of Sliding Window Log algorithm
class SlidingWindowRateLimiter {
    constructor(maxRequests, windowMs) {
        this.maxRequests = maxRequests;
        this.windowMs = windowMs;
        this.clients = new Map();
    }
    
    isAllowed(clientId) {
        const now = Date.now();
        const windowStart = now - this.windowMs;
        
        if (!this.clients.has(clientId)) {
            this.clients.set(clientId, [now]);
            return true;
        }
        
        const requests = this.clients.get(clientId);
        
        // Remove timestamps outside the current window
        while (requests.length > 0 && requests[0] <= windowStart) {
            requests.shift();
        }
        
        if (requests.length < this.maxRequests) {
            requests.push(now);
            return true;
        }
        
        return false; // Rate limit exceeded
    }
}
                

Advantage: Provides precise control but can use more memory as it needs to store timestamps for each request.

Token Bucket

The token bucket algorithm provides tokens at a steady rate and allows for controlled bursts of traffic.


// Conceptual implementation of Token Bucket algorithm
class TokenBucketRateLimiter {
    constructor(bucketSize, refillRate) {
        this.bucketSize = bucketSize;
        this.refillRate = refillRate; // tokens per millisecond
        this.clients = new Map();
    }
    
    isAllowed(clientId, tokens = 1) {
        const now = Date.now();
        
        if (!this.clients.has(clientId)) {
            // New client gets a full bucket
            this.clients.set(clientId, {
                tokens: this.bucketSize - tokens,
                lastRefill: now
            });
            return true;
        }
        
        const client = this.clients.get(clientId);
        
        // Refill tokens based on time elapsed
        const timeElapsed = now - client.lastRefill;
        const tokensToAdd = timeElapsed * this.refillRate;
        client.tokens = Math.min(client.tokens + tokensToAdd, this.bucketSize);
        client.lastRefill = now;
        
        if (client.tokens >= tokens) {
            client.tokens -= tokens;
            return true;
        }
        
        return false; // Not enough tokens
    }
}
                

Advantage: Handles bursts well while maintaining long-term rate limits.

Rate Limiting in Express.js

In Express.js applications, you can implement rate limiting using the popular express-rate-limit middleware:

Basic Rate Limiting


const rateLimit = require('express-rate-limit');
const app = express();

// Apply rate limiting to all requests
const limiter = rateLimit({
    windowMs: 15 * 60 * 1000, // 15 minutes
    max: 100, // Limit each IP to 100 requests per windowMs
    standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
    legacyHeaders: false, // Disable the `X-RateLimit-*` headers
    message: 'Too many requests from this IP, please try again after 15 minutes'
});

app.use(limiter);
                

Route-Specific Rate Limiting


// Apply stricter rate limiting to login endpoint
const loginLimiter = rateLimit({
    windowMs: 60 * 60 * 1000, // 1 hour
    max: 5, // 5 login attempts per hour
    message: 'Too many login attempts, please try again after an hour'
});

app.post('/login', loginLimiter, (req, res) => {
    // Login logic
});

// Apply different rate limits to API vs web pages
const apiLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100,
    message: { error: 'Too many requests' }
});

app.use('/api/', apiLimiter);
                

Advanced Rate Limiting Strategies

Beyond basic rate limiting, consider these advanced strategies:

Dynamic Rate Limits


// Rate limit based on user tier
const userTierLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: (req, res) => {
        // Get user tier from authenticated request
        if (req.user && req.user.tier === 'premium') {
            return 1000; // Premium users get higher limits
        }
        return 100; // Regular users get standard limits
    },
    keyGenerator: (req) => {
        // Use user ID instead of IP for authenticated users
        return req.user ? req.user.id : req.ip;
    }
});
                

Rate Limiting with Redis for Distributed Systems


const Redis = require('ioredis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
const redis = new Redis({
    host: 'redis-server',
    port: 6379
});

const rateLimiter = new RateLimiterRedis({
    storeClient: redis,
    keyPrefix: 'middleware',
    points: 10, // Number of points
    duration: 1, // Per second
});

app.use(async (req, res, next) => {
    try {
        // Consume 1 point per request
        await rateLimiter.consume(req.ip);
        next();
    } catch (err) {
        res.status(429).send('Too Many Requests');
    }
});
                

Real-World Example: GitHub API Rate Limiting

GitHub's REST API uses sophisticated rate limiting to ensure availability while allowing different usage patterns. Unauthenticated requests are limited to 60 per hour by IP address. Authenticated requests get 5,000 requests per hour per user. GitHub also implements a secondary rate limit to prevent abuse patterns, temporarily restricting actions when too many requests come in a short time. Their headers clearly communicate current limits, remaining requests, and reset times, helping developers build applications that respect these constraints.

DDoS Protection Strategies

Distributed Denial of Service (DDoS) attacks are coordinated attempts to make a service unavailable by overwhelming it with traffic from multiple sources. While rate limiting helps with certain types of DDoS attacks, comprehensive protection requires a multi-layered approach.

Types of DDoS Attacks

Defense in Depth

Effective DDoS protection requires multiple layers of defense:

flowchart TD A[DDoS Defense Layers] --> B[Network Level] A --> C[Server Level] A --> D[Application Level] A --> E[External Services] B --> B1[Firewall Rules] B --> B2[Traffic Filtering] B --> B3[Anycast Network] C --> C1[Resource Limits] C --> C2[Connection Timeouts] C --> C3[Load Balancing] D --> D1[Rate Limiting] D --> D2[CAPTCHA/Challenges] D --> D3[Request Validation] E --> E1[CDN Services] E --> E2[DDoS Mitigation Providers] E --> E3[Cloud WAF]

Network and Infrastructure Level Protection

These strategies operate at the lower levels of the network stack:

Server Level Protection

These measures focus on server configuration:

Nginx Configuration for DDoS Mitigation


http {
    # Basic settings
    client_body_timeout 10s;
    client_header_timeout 10s;
    keepalive_timeout 65s;
    send_timeout 10s;
    
    # Limit connections per IP
    limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
    limit_conn conn_limit_per_ip 20;
    
    # Limit requests per IP
    limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
    
    # Buffer size limitations
    client_body_buffer_size 200K;
    client_header_buffer_size 2k;
    large_client_header_buffers 2 1k;
    
    # Protect against slow HTTP attacks
    reset_timedout_connection on;
    
    server {
        listen 80;
        server_name example.com;
        
        # Apply rate limiting to specific endpoints
        location /login {
            limit_req zone=req_limit_per_ip burst=5 nodelay;
            # login handler
        }
        
        # Other configurations...
    }
}
                

Application Level Protection

These strategies operate within your application code:

Express.js DDoS Protection Strategies


const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
const app = express();

// Set security headers
app.use(helmet());

// Body size limits
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ extended: true, limit: '100kb' }));

// Rate limiting
const apiLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 100
});
app.use('/api/', apiLimiter);

// Speed limiting (gradually slows down responses when approaching rate limit)
const speedLimiter = slowDown({
    windowMs: 15 * 60 * 1000,
    delayAfter: 50,
    delayMs: 500 // 0.5 seconds
});
app.use('/api/', speedLimiter);

// Implement CAPTCHA for sensitive operations
app.post('/login', verifyCaptcha, (req, res) => {
    // Login logic
});

// Timeout for long operations
app.get('/search', (req, res) => {
    const searchTimeout = setTimeout(() => {
        res.status(503).send('Search operation timed out');
    }, 5000);
    
    performSearch(req.query)
        .then(results => {
            clearTimeout(searchTimeout);
            res.json(results);
        })
        .catch(err => {
            clearTimeout(searchTimeout);
            res.status(500).send('Error performing search');
        });
});

// Function to verify the CAPTCHA
function verifyCaptcha(req, res, next) {
    if (!req.body.captchaToken) {
        return res.status(400).send('CAPTCHA verification required');
    }
    
    // Verify with CAPTCHA provider
    verifyCaptchaToken(req.body.captchaToken)
        .then(valid => {
            if (valid) next();
            else res.status(400).send('CAPTCHA verification failed');
        })
        .catch(() => res.status(500).send('Error verifying CAPTCHA'));
}
                

Bot Detection and Management

Many DDoS attacks use automated bots, so detecting and filtering bot traffic is essential:

Simple Bot Detection with JavaScript Challenge


app.use((req, res, next) => {
    // Check if client has a valid verification token
    const clientToken = req.cookies.verificationToken;
    
    if (isValidToken(clientToken)) {
        // Token is valid, proceed
        return next();
    }
    
    // No valid token, serve JavaScript challenge page
    res.send(`
        <html>
        <head><title>Verification</title></head>
        <body>
            <p>Please wait while we verify your browser...</p>
            <script>
                // Generate a token based on browser characteristics
                function generateToken() {
                    const canvas = document.createElement('canvas');
                    const gl = canvas.getContext('webgl');
                    const browserData = {
                        userAgent: navigator.userAgent,
                        language: navigator.language,
                        resolution: \`\${screen.width}x\${screen.height}\`,
                        webglRenderer: gl ? gl.getParameter(gl.RENDERER) : null,
                        timezone: new Date().getTimezoneOffset()
                    };
                    
                    // This would actually hash the data in a real implementation
                    return btoa(JSON.stringify(browserData));
                }
                
                // Set token cookie and redirect back to original URL
                const token = generateToken();
                document.cookie = \`verificationToken=\${token}; path=/\`;
                window.location.reload();
            </script>
        </body>
        </html>
    `);
});

function isValidToken(token) {
    if (!token) return false;
    
    try {
        // Decode token and check if it's valid
        // In a real implementation, you'd verify it against a stored value or signature
        const decodedData = JSON.parse(atob(token));
        
        // Basic validation: check if required fields exist
        return decodedData.userAgent && decodedData.resolution;
    } catch (e) {
        return false;
    }
}
                

Using Cloud Services for DDoS Protection

For comprehensive protection, especially against large-scale attacks, cloud-based DDoS protection services are invaluable:

Real-World Example: Cloudflare vs. Record DDoS Attack

In February 2024, Cloudflare reported mitigating one of the largest HTTP DDoS attacks ever recorded. The attack peaked at 704 million requests per second (rps), targeting a gaming company's website. The attack used a botnet of over 255,000 unique IP addresses across 172 countries. Despite the enormous scale, Cloudflare's distributed network was able to absorb and filter the attack traffic, keeping the target website online and responsive. This example highlights the effectiveness of cloud-based DDoS protection services against attacks that would overwhelm most self-hosted defenses.

Implementing a Defense Strategy

Let's build a comprehensive defense strategy for a typical web application, combining rate limiting with DDoS protection:

Multi-Layered Defense Implementation


const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const slowDown = require('express-slow-down');
const { RateLimiterRedis } = require('rate-limiter-flexible');
const Redis = require('ioredis');
const cookieParser = require('cookie-parser');
const app = express();

// Connect to Redis for distributed rate limiting
const redis = new Redis(process.env.REDIS_URL);

// Parse cookies for token validation
app.use(cookieParser());

// Set security headers
app.use(helmet());

// Body size limits
app.use(express.json({ limit: '100kb' }));
app.use(express.urlencoded({ extended: true, limit: '100kb' }));

// ---------- Bot Detection Middleware ----------
app.use((req, res, next) => {
    // Skip bot detection for static assets
    if (req.path.match(/\.(css|js|jpg|png|gif|ico)$/)) {
        return next();
    }
    
    // Verify client token
    const clientToken = req.cookies.clientToken;
    if (isValidToken(clientToken)) {
        return next();
    }
    
    // Check if this is an API request
    if (req.path.startsWith('/api/')) {
        return res.status(403).json({ error: 'Access denied' });
    }
    
    // Serve challenge page for other routes
    serveJavaScriptChallenge(res);
});

// ---------- Global Rate Limiting ----------
// Basic rate limit by IP
const globalLimiter = rateLimit({
    windowMs: 5 * 60 * 1000, // 5 minutes
    max: 500, // 500 requests per 5 minutes
    standardHeaders: true,
    message: 'Too many requests, please try again later'
});
app.use(globalLimiter);

// Speed limiter for all routes
const speedLimiter = slowDown({
    windowMs: 5 * 60 * 1000,
    delayAfter: 300, // Allow 300 requests per 5 minutes at full speed
    delayMs: (hits) => hits * 50 // Add 50ms delay per request over limit
});
app.use(speedLimiter);

// ---------- Advanced Rate Limiting for Sensitive Routes ----------
// Create a more sophisticated rate limiter for login
const loginLimiter = new RateLimiterRedis({
    storeClient: redis,
    keyPrefix: 'login_limit',
    points: 5, // 5 attempts
    duration: 60 * 60, // Per hour
    blockDuration: 60 * 60, // Block for 1 hour after exceeding
});

// Apply to login route
app.post('/api/login', async (req, res, next) => {
    const key = req.ip;
    
    try {
        await loginLimiter.consume(key);
        // Continue to login logic
        next();
    } catch (err) {
        if (err.msBeforeNext) {
            const minutesBeforeNext = Math.ceil(err.msBeforeNext / 60000);
            res.set('Retry-After', String(minutesBeforeNext * 60));
            res.status(429).json({
                error: 'Too many login attempts',
                retryAfter: `${minutesBeforeNext} minutes`
            });
        } else {
            res.status(500).json({ error: 'Internal error' });
        }
    }
});

// ---------- API-Specific Rate Limits ----------
// Rate limiter for search endpoint (which might be resource-intensive)
const searchLimiter = new RateLimiterRedis({
    storeClient: redis,
    keyPrefix: 'search_limit',
    points: 20, // 20 searches
    duration: 60, // Per minute
});

app.get('/api/search', async (req, res, next) => {
    // Get user ID or IP address
    const key = req.user ? req.user.id : req.ip;
    
    try {
        // Consume more points for complex searches
        const complexity = calculateSearchComplexity(req.query);
        await searchLimiter.consume(key, complexity);
        
        // Set timeout for long-running searches
        const searchTimeout = setTimeout(() => {
            res.status(503).send('Search operation timed out');
        }, 5000);
        
        // Perform the search
        const results = await performSearch(req.query);
        clearTimeout(searchTimeout);
        res.json(results);
    } catch (err) {
        if (err.msBeforeNext) {
            res.status(429).json({
                error: 'Search rate limit exceeded',
                retryAfter: Math.ceil(err.msBeforeNext / 1000)
            });
        } else {
            res.status(500).json({ error: 'Search error' });
        }
    }
});

// ---------- User-Tier Based Rate Limiting ----------
// Differentiated rate limits based on user tier
app.use('/api/premium', async (req, res, next) => {
    if (!req.user) {
        return res.status(401).json({ error: 'Authentication required' });
    }
    
    const key = req.user.id;
    const tier = req.user.tier || 'free';
    
    // Different limits based on user tier
    const tierLimits = {
        free: { points: 50, duration: 3600 },
        basic: { points: 200, duration: 3600 },
        premium: { points: 1000, duration: 3600 },
        enterprise: { points: 5000, duration: 3600 }
    };
    
    const limit = tierLimits[tier] || tierLimits.free;
    
    try {
        // Create or get limiter for this tier
        const tierLimiter = new RateLimiterRedis({
            storeClient: redis,
            keyPrefix: `tier_${tier}`,
            points: limit.points,
            duration: limit.duration
        });
        
        await tierLimiter.consume(key);
        next();
    } catch (err) {
        if (err.msBeforeNext) {
            res.status(429).json({
                error: `Rate limit exceeded for ${tier} tier`,
                retryAfter: Math.ceil(err.msBeforeNext / 1000),
                upgradeUrl: tier !== 'enterprise' ? '/account/upgrade' : null
            });
        } else {
            res.status(500).json({ error: 'Internal error' });
        }
    }
});

// ---------- Helper Functions ----------
function isValidToken(token) {
    // Implement token validation logic
    // In production, you would verify signatures, timestamps, etc.
    if (!token) return false;
    
    try {
        const decoded = JSON.parse(Buffer.from(token, 'base64').toString());
        return decoded.userAgent && Date.now() - decoded.timestamp < 86400000; // Valid for 24 hours
    } catch (e) {
        return false;
    }
}

function serveJavaScriptChallenge(res) {
    // In production, this would be more sophisticated
    res.send(`
        <html>
        <head><title>Verification</title></head>
        <body>
            <p>Please wait while we verify your browser...</p>
            <script>
                const token = btoa(JSON.stringify({
                    userAgent: navigator.userAgent,
                    timestamp: Date.now()
                }));
                document.cookie = \`clientToken=\${token}; path=/; max-age=86400\`;
                window.location.reload();
            </script>
        </body>
        </html>
    `);
}

function calculateSearchComplexity(query) {
    // Calculate how resource-intensive a search might be
    let complexity = 1;
    
    // More fields = more complex
    complexity += Object.keys(query).length * 0.5;
    
    // Wildcard searches are more expensive
    if (query.q && query.q.includes('*')) {
        complexity += 2;
    }
    
    // Large date ranges are expensive
    if (query.startDate && query.endDate) {
        const start = new Date(query.startDate);
        const end = new Date(query.endDate);
        const daysDiff = Math.abs((end - start) / (1000 * 60 * 60 * 24));
        complexity += Math.min(daysDiff / 30, 5); // Max 5 points for date range
    }
    
    return Math.max(1, Math.ceil(complexity));
}

// Start the server
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});
                

Monitoring and Response Planning

Even with the best preventative measures, you need to monitor for attacks and have a response plan:

Monitoring Traffic Patterns

Building an Incident Response Plan

  1. Detection: Set up alerts for traffic anomalies and threshold breaches
  2. Classification: Determine the type and severity of the attack
  3. Containment: Implement emergency measures to mitigate the impact
  4. Communication: Notify stakeholders and, if necessary, users
  5. Eradication: Block attack sources and implement additional protections
  6. Recovery: Restore normal operations and monitor for recurring issues
  7. Post-Mortem: Analyze what happened and improve defenses

Emergency Response Techniques

Dynamic Defense Adjustments


// System to dynamically adjust rate limits based on server load
const os = require('os');
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);

// Monitor system load every 5 seconds
setInterval(async () => {
    // Check current system load
    const currentLoad = os.loadavg()[0]; // 1-minute load average
    const cpuCount = os.cpus().length;
    const loadPerCPU = currentLoad / cpuCount;
    
    // Set rate limit policy based on load
    let ratePolicy = 'normal';
    
    if (loadPerCPU > 0.7 && loadPerCPU <= 0.85) {
        ratePolicy = 'moderate';
    } else if (loadPerCPU > 0.85) {
        ratePolicy = 'aggressive';
    }
    
    // Store current policy in Redis for rate limiters to use
    await redis.set('rate_limit_policy', ratePolicy);
    
    // Log policy changes
    console.log(`Load: ${loadPerCPU.toFixed(2)}, Policy: ${ratePolicy}`);
}, 5000);

// Middleware to apply dynamic rate limits
app.use(async (req, res, next) => {
    // Skip for certain endpoints
    if (req.path.match(/\.(css|js|jpg|png|gif|ico)$/)) {
        return next();
    }
    
    // Get current rate limit policy
    const policy = await redis.get('rate_limit_policy') || 'normal';
    
    // Apply different limits based on policy
    const limits = {
        normal: { windowMs: 60000, max: 100 },
        moderate: { windowMs: 60000, max: 50 },
        aggressive: { windowMs: 60000, max: 20 }
    };
    
    const currentLimit = limits[policy];
    
    // Apply rate limiting
    const limiter = rateLimit({
        windowMs: currentLimit.windowMs,
        max: currentLimit.max,
        message: `Server is experiencing high load. Please try again later.`
    });
    
    return limiter(req, res, next);
});
                

Real-World Example: GitHub's Response to the 2018 Memcached DDoS

In February 2018, GitHub experienced the largest DDoS attack ever recorded at that time, peaking at 1.35 Tbps using a Memcached amplification attack. GitHub's response was exemplary: their monitoring systems detected the attack within minutes, they automatically activated their DDoS mitigation service (Akamai Prolexic), and traffic was rerouted through scrubbing centers to filter out malicious packets. Within ten minutes of detection, the attack was mitigated, and GitHub remained accessible to legitimate users. This demonstrates the importance of both monitoring and having an automated response plan in place.

Best Practices

Rate Limiting Best Practices

DDoS Mitigation Best Practices

Common Anti-Patterns to Avoid

Practical Exercises

Exercise 1: Implement Basic Rate Limiting

Create an Express.js application with the following rate limiting features:

Exercise 2: Dynamic Rate Limiting

Extend your rate limiting implementation to include:

Exercise 3: Basic DDoS Protection

Implement basic DDoS protection measures:

Exercise 4: Build a Response Plan

Create a comprehensive incident response plan for a DDoS attack:

Additional Resources

Next Class Preparation

For our next session on Full Stack Application Development, please: