fix(security): harden AgentDriveArchiveMemberApi signed-URL verification
- Dominant language
- TypeScript
- Stars
- 156k
- Forks
- 24.6k
- Avg merge
- 22h 9m
- Merged PRs (30d)
- 610
Description
## AgentDriveArchiveMemberApi signed-URL verification is non-constant-time, accepts future timestamps, and has no replay protection
`api/services/agent_drive_service.py` `verify_archive_member_signature` is the sole authorization on the unauthenticated `GET /files/agent-drive/archive-member` endpoint. Three weaknesses in the verification together let an attacker who intercepts a signed URL (logs, browser history, referer leaks, accidental sharing) read the underlying archive bytes indefinitely and also brute-force the signature itself.
### Issues
1. **Non-constant-time signature comparison** (`agent_drive_service.py:1206`). The verification uses `sign != cls._sign_archive_member_payload(payload)`, which short-circuits on the first mismatching byte and leaks signature bytes via timing. The signature can be brute-forced one byte at a time from a network-adjacent position.
2. **No lower-bound timestamp check** (`agent_drive_service.py:1209`). Only the upper bound is enforced (`current_time - int(timestamp) <= FILES_ACCESS_TIMEOUT`). A captured URL with a forged far-future timestamp stays valid indefinitely. There is no check that the timestamp is not in the future, even with a small clock-skew allowance.
3. **No replay protection.** The `nonce` field is part of the signed payload but is never recorded server-side, so a captured URL is fully replayable for the entire access window.
### Attack scenarios
- Intercept any signed archive-member URL — from server access logs, browser history, accidental sharing, or referer leakage — and reuse it indefinitely. Default `FILES_ACCESS_TIMEOUT` is 300 seconds; the lack of nonce tracking means a single interception is enough.
- Brute-force the signature one byte at a time by measuring response time. The endpoint is unauthenticated, so the attacker can probe from any IP.
### Fix
1. Replace `sign != ...` with `hmac.compare_digest(sign, ...)`.
2. Reject timestamps that are more than a small clock-skew window in the future, in addition to the existing upper-bound check.
3. Record the nonce in Redis with the same TTL as the access window using `SET ... NX` for atomic first-use semantics; repeat attempts find the key already present and are rejected.
Contributor guide
Research direction
Start in api/services/agent_drive_service.py at verify_archive_member_signature and the unauthenticated GET /files/agent-drive/archive-member entry point. Trace the existing timestamp and payload checks, then review the Redis access pattern needed for atomic nonce recording. Done means signature comparison is constant-time, future timestamps are rejected within a clock-skew window, and nonce reuse is rejected for the access window.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, redis
- Domain
- api, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100