API Rate Limiting Bypass and Quota Circumvention
API endpoints that implement weak or bypassable rate limiting mechanisms allow attackers to exceed intended usage limits through various techniques including request distribution, header manipulation, or exploiting rate limit implementation flaws.
Preview example – JAVASCRIPT
// VULNERABLE: API rate limiting with multiple bypass opportunities
const express = require('express');
const rateLimit = require('express-rate-limit');
const app = express();
// PROBLEM 1: Simple rate limiting with easy bypasses
const basicRateLimit = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // PROBLEM: Fixed limit without user context
message: 'Too many requests',
keyGenerator: (req) => {
// VULNERABLE: Only using IP address
return req.ip;
},
skip: (req) => {
// PROBLEM: Admin bypass without proper validation
return req.headers['x-admin-bypass'] === 'true';
}
});
// VULNERABLE: Rate limiting applied inconsistently
app.use('/api/public', basicRateLimit);
// PROBLEM 2: No rate limiting on critical endpoints
app.post('/api/upload', async (req, res) => {
try {
// VULNERABLE: No upload rate limiting
const file = req.file;
// PROBLEM: File size check after upload
if (file.size > 10 * 1024 * 1024) { // 10MB
return res.status(413).json({ error: 'File too large' });
}
// PROBLEM: No user quota validation
const result = await processFileUpload(file, req.user.id);
res.json({ success: true, fileId: result.id });
} catch (error) {
res.status(500).json({ error: 'Upload failed' });
}
});
// PROBLEM 3: Per-user quotas not enforced
class UserQuotaManager {
constructor() {
this.quotas = new Map(); // In-memory storage is problematic
}
// VULNERABLE: Quota checking without atomic operations
async checkUserQuota(userId, resourceType, requestedAmount) {
const userQuota = await this.getUserQuota(userId, resourceType);
const currentUsage = await this.getCurrentUsage(userId, resourceType);
// PROBLEM: Check-then-act race condition
if (currentUsage + requestedAmount > userQuota.limit) {
return {
allowed: false,
reason: 'Quota exceeded',
current: currentUsage,
limit: userQuota.limit
};
}
// VULNERABLE: Usage update separate from check
await this.incrementUsage(userId, resourceType, requestedAmount);
return {
allowed: true,
remaining: userQuota.limit - currentUsage - requestedAmount
};
}
// PROBLEM: Usage tracking without persistence
async incrementUsage(userId, resourceType, amount) {
const key = `${userId}:${resourceType}`;
const current = this.quotas.get(key) || 0;
this.quotas.set(key, current + amount);
// PROBLEM: No database persistence
return current + amount;
}
}
const quotaManager = new UserQuotaManager();
// VULNERABLE: Bulk API operations without proper limiting
app.post('/api/bulk-process', async (req, res) => {
const { operations } = req.body;
// PROBLEM: No validation of bulk operation size
if (!Array.isArray(operations)) {
return res.status(400).json({ error: 'Invalid operations format' });
}
const results = [];
// VULNERABLE: Processing unlimited operations
for (const operation of operations) {
try {
// PROBLEM: No individual operation rate limiting
const result = await processOperation(operation, req.user.id);
results.push({ success: true, result });
} catch (error) {
results.push({ success: false, error: error.message });
}
}
res.json({ results });
});
// PROBLEM 4: WebSocket connections without limits
const http = require('http');
const socketIo = require('socket.io');
const server = http.createServer(app);
const io = socketIo(server);
// VULNERABLE: Unlimited WebSocket connections
io.on('connection', (socket) => {
console.log('User connected:', socket.id);
// PROBLEM: No connection limits per user
socket.on('subscribe', (data) => {
// VULNERABLE: Unlimited subscriptions
socket.join(data.channel);
});
socket.on('message', async (data) => {
// PROBLEM: No message rate limiting
try {
await processMessage(data, socket.userId);
io.to(data.channel).emit('message', data);
} catch (error) {
socket.emit('error', { message: error.message });
}
});
// PROBLEM: No cleanup of user resources on disconnect
socket.on('disconnect', () => {
console.log('User disconnected:', socket.id);
});
});
// VULNERABLE: Pagination without limits
app.get('/api/data', async (req, res) => {
const {
page = 1,
limit = 10, // PROBLEM: Default limit too low, no maximum
sortBy,
filter
} = req.query;
try {
// PROBLEM: No maximum limit validation
const pageLimit = parseInt(limit);
const pageNumber = parseInt(page);
// VULNERABLE: Can request massive page sizes
const results = await Database.find(filter)
.sort(sortBy)
.skip((pageNumber - 1) * pageLimit)
.limit(pageLimit);
res.json({
data: results,
page: pageNumber,
limit: pageLimit,
total: await Database.countDocuments(filter)
});
} catch (error) {
res.status(500).json({ error: 'Data retrieval failed' });
}
});
// Attack scenarios:
// 1. Use different IP addresses to bypass rate limits
// 2. Manipulate headers to trigger admin bypass
// 3. Submit massive bulk operations
// 4. Request huge page sizes to exhaust memory
// 5. Open unlimited WebSocket connections
// 6. Race conditions in quota checking