CMMC 2.0 SC.L1-3.13.1 (NIST 800-171r2 3.13.1) requires that communications are monitored and controlled at the external boundary of systems containing FCI. Without rate limiting or request filtering on API endpoints — particularly authentication routes — brute-force credential stuffing attacks succeed unchecked. CWE-799 (Improper Control of Interaction Frequency) and OWASP A05 (Security Misconfiguration) both name boundary control gaps as exploitable. A login endpoint with no rate limit and no logging allows thousands of credential attempts per minute, making any account with a weak password reachable within seconds.
High because the absence of boundary controls leaves authentication endpoints open to automated brute-force attacks with no detection or throttling mechanism.
Add rate limiting to all API endpoints. Use a Redis-backed limiter for production scale — in-memory rate limiters reset on cold starts and are unsuitable for multi-instance deployments:
// lib/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
export const authRateLimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, '60 s'),
analytics: true,
})
// app/api/auth/login/route.ts
export async function POST(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1'
const { success } = await authRateLimit.limit(ip)
if (!success) {
return Response.json(
{ error: 'Too many login attempts. Try again in 60 seconds.' },
{ status: 429, headers: { 'Retry-After': '60' } }
)
}
// Log the attempt for boundary monitoring
console.log(JSON.stringify({ event: 'auth_attempt', ip, ts: new Date().toISOString() }))
// ... proceed with login
}
Vercel and Cloudflare WAF rules provide additional boundary controls at the hosting layer — configure them in addition to application-level rate limiting.
ID: gov-cmmc-level-1.system-comms.boundary-protection
Severity: high
CMMC Practice: SC.L1-3.13.1
What to look for: Look for rate limiting, request filtering, and WAF or API gateway configuration. Examine middleware for rate limiting implementation (express-rate-limit, @upstash/ratelimit, custom token bucket, or similar). Check whether API endpoints validate request content types. Look for any intrusion detection or anomalous request filtering. Check hosting config (vercel.json, cloudflare workers config, nginx.conf) for boundary controls. Review whether the application logs failed authentication attempts or unusual access patterns.
Pass criteria: Count all API endpoints and check each for rate limiting and content-type validation. Rate limiting is implemented on at least 80% of API endpoints, particularly authentication endpoints. At least 1 security boundary control is visible in code or hosting configuration. Report: "X of Y API endpoints have rate limiting; Z boundary controls configured."
Fail criteria: No rate limiting on any API endpoints. No request filtering or content-type validation. No evidence of boundary monitoring or intrusion detection configuration. Authentication brute-force attacks would succeed unchecked.
Skip (N/A) when: Static site with no API endpoints and no dynamic server processing.
Detail on fail: Specify which boundary protections are absent. Example: "No rate limiting found in middleware.ts or any API routes. POST /api/auth/login accepts unlimited requests — brute force possible. No request filtering or content-type validation." Keep under 500 characters.
Remediation: Add rate limiting to all API endpoints, especially authentication routes:
// lib/rate-limit.ts
// Using @upstash/ratelimit (Redis-backed, works with Vercel Edge)
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
export const authRateLimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, '60 s'),
analytics: true
})
// app/api/auth/login/route.ts
import { authRateLimit } from '@/lib/rate-limit'
export async function POST(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1'
const { success, remaining } = await authRateLimit.limit(ip)
if (!success) {
return Response.json(
{ error: 'Too many login attempts. Try again in 60 seconds.' },
{ status: 429, headers: { 'Retry-After': '60' } }
)
}
console.log(JSON.stringify({
event: 'auth_attempt',
ip,
remaining,
timestamp: new Date().toISOString()
}))
// ... proceed with login
}