#23692: fix(pairing): add brute-force protection for DM pairing codes
size: M
trusted-contributor
Cluster:
Messaging Platform Improvements
## 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
#16773: fix(security): OC-100 add rate limiting to pairing code verification
by aether-ai-agent · 2026-02-15
87.1%
#23418: Pairing: add persistent sender and IP backoff controls
by bmendonca3 · 2026-02-22
76.4%
#7911: Unapproved users receive pairing codes on every /start message befo...
by crazypeace · 2026-02-03
74.9%
#11249: fix(whatsapp): prevent pairing-mode auto-replies to unknown DMs
by liuxiaopai-ai · 2026-02-07
74.7%
#22636: fix(whatsapp): skip pairing store merge when dmPolicy is allowlist (#…
by anillBhoi · 2026-02-21
74.7%
#13696: feat(cli): add --code option for WhatsApp pairing code login
by asklee-klawd · 2026-02-10
72.7%
#13285: feat(pairing): add pairingAnonymous option to hide platform branding
by thebtf · 2026-02-10
72.1%
#21697: fix(gateway): unblock local spawn pairing and gated private-LAN ws
by rjuanluis · 2026-02-20
71.7%
#21148: fix(gateway): add request-aware pairing recovery hints and docs
by cluster2600 · 2026-02-19
71.6%
#13090: fix: Device-pair extension leaks gateway credentials in chat messages
by coygeek · 2026-02-10
71.6%