Login, password reset, and account creation endpoints without IP-based rate limiting are open to brute-force credential stuffing and enumeration attacks. CWE-307 (Improper Restriction of Excessive Authentication Attempts) is the direct classification. OWASP API Security Top 10 2023 API4 (Unrestricted Resource Consumption) applies because password reset emails and SMS OTPs are finite, paid resources attackers can exhaust. Credential stuffing tools spray millions of username/password combinations against unthrottled login endpoints. Without rate limiting, an attacker with a breached credential list can test thousands of combinations per minute from a single IP.
Low because brute force on unauthenticated endpoints is mitigated by strong passwords and MFA — but absent those controls, unthrottled login endpoints become the attack path.
Apply tight IP-based rate limits specifically on auth-critical unauthenticated endpoints. Use a shorter window and lower threshold than general API rate limits.
// src/app/api/auth/login/route.ts
import { ipRateLimit } from '@/lib/rate-limit'
export async function POST(req: Request) {
const ip = req.headers.get('x-forwarded-for')?.split(',')[0] ?? 'unknown'
const allowed = await ipRateLimit(ip, { max: 5, windowSeconds: 900 }) // 5 per 15 min
if (!allowed) {
return Response.json(
{ error: 'Too many attempts. Try again in 15 minutes.' },
{ status: 429, headers: { 'Retry-After': '900' } }
)
}
// ... authentication logic
}
Apply the same limiter to /api/auth/forgot-password and /api/auth/register to prevent email/SMS bombing.
ID: api-security.abuse-prevention.rate-limiting-ip
Severity: low
What to look for: Enumerate every relevant item. Check if unauthenticated endpoints (especially login, password reset, password endpoints) have rate limiting based on IP address to prevent brute force attacks.
Pass criteria: At least 1 of the following conditions is met. Unauthenticated endpoints like login and password reset are rate-limited by IP address. Brute force attacks are mitigated by returning 429 after repeated failed attempts.
Fail criteria: Unauthenticated endpoints have no rate limiting, allowing rapid-fire login attempts or password reset spam.
Skip (N/A) when: The API has no unauthenticated endpoints or uses alternative auth mechanisms like TOTP that are resistant to brute force.
Detail on fail: "Login endpoint has no rate limiting — attackers can brute force credentials" or "Password reset endpoint accepts unlimited requests from same IP"
Remediation: Add IP-based rate limiting to sensitive unauthenticated endpoints:
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5, // 5 attempts per 15 minutes
keyGenerator: (req) => req.ip, // Rate limit per IP
message: 'Too many login attempts, please try again later'
})
app.post('/api/login', loginLimiter, async (req, res) => {
// Login logic
})