From harness-claude
Implements rate limiting and throttling for Express APIs using express-rate-limit and Redis to prevent brute force attacks, scraping, API abuse, and DoS on auth endpoints and resource-intensive operations.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Protect APIs with rate limiting, throttling, and abuse prevention to mitigate brute force, scraping, and denial of service
Implements rate limiting with token bucket, sliding window, and fixed window algorithms using Redis for Express APIs. Protects against abuse, overload, and enforces per-user/IP quotas.
Implements API rate limiting using token bucket, sliding window, Redis algorithms, and Express middleware. Use for securing public APIs, tiered access, and DoS protection.
Implements API rate limiting and throttling using token bucket, sliding window, fixed window algorithms with Redis counters or middleware. Protects against brute force, abuse, DoS attacks.
Share bugs, ideas, or general feedback.
Protect APIs with rate limiting, throttling, and abuse prevention to mitigate brute force, scraping, and denial of service
import rateLimit from 'express-rate-limit';
// General API rate limit
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true, // Return rate limit info in RateLimit-* headers
legacyHeaders: false,
message: { error: 'Too many requests. Please try again later.' },
});
// Strict limit for authentication endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 10, // 10 login attempts per 15 minutes
skipSuccessfulRequests: true, // Only count failed attempts
keyGenerator: (req) => req.body?.email ?? req.ip, // Rate limit per email, not just IP
});
// Strict limit for password reset
const resetLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 3, // 3 reset requests per hour
});
app.use('/api/', apiLimiter);
app.post('/api/auth/login', authLimiter, loginHandler);
app.post('/api/auth/reset-password', resetLimiter, resetHandler);
import { RedisStore } from 'rate-limit-redis';
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
store: new RedisStore({
sendCommand: (...args: string[]) => redis.call(...args),
}),
});
async function loginWithProgressiveDelay(email: string, password: string, ip: string) {
const key = `login_attempts:${email}`;
const attempts = await redis.incr(key);
await redis.expire(key, 3600); // Reset after 1 hour
if (attempts > 10) {
throw new TooManyRequestsError('Account temporarily locked. Try again in 1 hour.');
}
if (attempts > 3) {
// Progressive delay: 1s, 2s, 4s, 8s...
const delayMs = Math.min(1000 * Math.pow(2, attempts - 4), 30000);
await new Promise((resolve) => setTimeout(resolve, delayMs));
}
const user = await authenticate(email, password);
if (user) {
await redis.del(key); // Reset on success
return user;
}
logger.warn({ event: 'auth.login.failure', email, ip, attempts }, 'Login failed');
throw new UnauthorizedError('Invalid credentials');
}
RateLimit-Limit: 100
RateLimit-Remaining: 42
RateLimit-Reset: 1620000000
Retry-After: 120
const keyGenerator = (req: Request) => {
return req.user?.id ?? req.ip;
};
import { RateLimiterRedis } from 'rate-limiter-flexible';
const rateLimiter = new RateLimiterRedis({
storeClient: redis,
keyPrefix: 'api_limit',
points: 100, // 100 requests
duration: 60, // per 60 seconds
blockDuration: 60, // block for 60 seconds if exceeded
execEvenly: true, // distribute points evenly over duration
});
app.use(async (req, res, next) => {
try {
const key = req.user?.id ?? req.ip;
const result = await rateLimiter.consume(key);
res.setHeader('RateLimit-Limit', 100);
res.setHeader('RateLimit-Remaining', result.remainingPoints);
res.setHeader('RateLimit-Reset', new Date(Date.now() + result.msBeforeNext).toISOString());
next();
} catch (rejRes) {
res.status(429).json({
error: 'Too many requests',
retryAfter: Math.ceil(rejRes.msBeforeNext / 1000),
});
}
});
const costs: Record<string, number> = {
'GET /api/users/:id': 1,
'GET /api/search': 5,
'POST /api/export': 20,
'POST /api/upload': 10,
};
app.use(async (req, res, next) => {
const routeKey = `${req.method} ${req.route?.path ?? req.path}`;
const cost = costs[routeKey] ?? 1;
try {
await rateLimiter.consume(req.user?.id ?? req.ip, cost);
next();
} catch {
res.status(429).json({ error: 'Rate limit exceeded' });
}
});
Rate limiting algorithms:
Where to rate limit:
Layer 1-2 for DDoS protection, Layer 3-4 for business logic rate limiting.
Common mistakes:
https://cheatsheetseries.owasp.org/cheatsheets/Denial_of_Service_Cheat_Sheet.html