2.9 KiB
Phase: 8 Sequence: 004 Slug: rate-limit-substring-bypass Verdict: VALID Rationale: Rate limiting sensitive path detection uses substring matching (path.includes) with incomplete sensitive list; sensitive operations like darkwatch.runScan and voiceprint.analyzeAudio get standard tier (100/min) instead of stricter limits Severity-Original: medium Severity: medium PoC-Status: pending Pre-FP-Flag: none Debate: piolium/attack-surface/balanced-chamber-summary.md
Summary
The rate limiting middleware in web/src/server/api/utils.ts detects sensitive paths using path.includes(p) where p is from a hardcoded list of sensitive operation names (["login", "signup", "forgotPassword", "resetPassword"]). This substring matching is imprecise and the sensitive list is incomplete — it only covers auth-related operations. Sensitive operations like darkwatch.runScan (triggers expensive external API calls), voiceprint.analyzeAudio (processes audio through ML), and spamshield.classifySMS get the standard authenticated tier (100/min) instead of a stricter sensitive tier (3/hr).
Location
web/src/server/api/utils.tslines 35–38 (rate limiting middleware)
Attacker Control
Any authenticated user can call sensitive operations at the higher rate limit (100/min) since they are not in the sensitive path list. The attacker does not need to craft special procedure paths — they simply use normal operations that are not covered by the sensitive list.
Trust Boundary Crossed
Rate limiting policy boundary. The rate limiter applies different limits based on operation sensitivity, and the incomplete sensitive list allows operations that should be rate-limited to proceed at higher rates.
Impact
Resource exhaustion and cost abuse for sensitive operations:
darkwatch.runScancan be called 100 times/min instead of 3/hr, triggering expensive external API calls (HIBP, SecurityTrails, Censys, Shodan)voiceprint.analyzeAudiocan be called 100 times/min, consuming memory and CPU for ML processing- Service disruption for other users on the same server
Evidence
const sensitivePaths = ["login", "signup", "forgotPassword", "resetPassword"];
const effectiveTier = sensitivePaths.some((p) => path.includes(p)) ? "sensitive" : tier;
Reproduction Steps
- Authenticated user calls
darkwatch.runScanwith a watchlist item - The procedure path
"darkwatch.runScan"does not contain any sensitive path substring - Rate limiter assigns
authenticatedtier (100/min) instead ofsensitivetier (3/hr) - User can trigger 100 scans per minute, each triggering 5+ external API calls
- Cumulative cost and resource impact affects all users
Defense Search Results
path.includes(p)substring matching is imprecise- Sensitive list only covers auth-related operations
rateLimitedProceduremiddleware is not applied to all procedures- No default sensitive tier for write operations (mutations)
- No IP-based rate limiting as secondary dimension