← Back to PRs

#23692: fix(pairing): add brute-force protection for DM pairing codes

by widingmarcus-cyber open 2026-02-22 16:00 View on GitHub →
size: M trusted-contributor
## Summary The DM pairing code system uses 8-character codes from a 32-character alphabet (~40 bits of entropy) with a 1-hour TTL but **no rate limiting** on approval attempts. An attacker who triggers a pairing request can systematically brute-force the code. ## Changes Two layers of protection: ### 1. Per-channel rate limiting (in-memory) After **10 consecutive** failed approval attempts on a channel, block further attempts for **5 minutes**. Resets on successful approval or gateway restart. ### 2. Per-request attempt tracking (persisted) Each pairing request tracks `failedAttempts`. After **5 failed guesses** against a specific request, it is automatically expired and removed from the store. ## Why these thresholds - **10 channel failures / 5 per-request:** Generous enough that legitimate owners who mistype codes won't be locked out, while making brute-force infeasible. With 32^8 (~1.1 trillion) possible codes and max 10 attempts per 5 minutes, exhaustive search would take ~10.4 million years. - **In-memory rate state:** No Redis dependency. Resets on restart, which is acceptable since the pairing codes themselves have a 1-hour TTL. - **Persisted per-request counter:** Survives restarts, ensuring an attacker can't reset the counter by waiting for a gateway bounce. ## Tests 5 regression tests: 1. Blocks after 10 consecutive failures (channel-level) 2. Expires request after 5 failed guesses (per-request) 3. Allows approval under threshold 4. Resets counter after successful approval 5. Isolates rate limiting between channels All 15 pairing tests pass (10 existing + 5 new). ## References - Fixes #16458 - CWE-307: Improper Restriction of Excessive Authentication Attempts - Related to #22996 (WebSocket pairing auth) <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds dual-layer brute-force protection to the DM pairing code approval system to address CWE-307. **Protection mechanisms:** - **Channel-level rate limiting (in-memory):** Blocks approval attempts for 5 minutes after 10 consecutive failures per channel+accountId combination. Resets on successful approval or cooldown expiration. - **Per-request attempt tracking (persisted):** Expires individual pairing requests after 5 failed guesses against them, preventing targeted attacks on specific codes. **Key observations:** - The implementation correctly increments `failedAttempts` on all requests matching the `accountId` filter when a wrong code is provided, ensuring per-request limits work as intended - In-memory rate-limit state resets on gateway restart (acceptable given 1-hour TTL on codes) - Per-request counters persist to disk, surviving restarts - Rate limiting is properly isolated between channels - Tests comprehensively cover the attack vectors and edge cases <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no identified issues - The implementation is well-designed with dual-layer protection, comprehensive test coverage (5 new tests covering all attack vectors), and correct logic. The chosen thresholds (10 channel failures, 5 per-request) make brute-force attacks computationally infeasible while avoiding false positives for legitimate users. The code follows repository patterns and includes appropriate comments. - No files require special attention <sub>Last reviewed commit: 63d53aa</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs