5.4 KiB
5.4 KiB
TASK METADATA
- Target Files:
server/pow.tsserver/routes/events.tsserver/valkey.tsui/components/events/JoinPage.tsxui/components/events/JoinScript.tsxui/components/events/EventLandingPage.tsxserver/tests/pow.test.ts
- Core Objective: Implement a transparent, sub-50ms client-side WebCrypto
(
crypto.subtle) SHA-256 Proof-of-Work (PoW) ingress shield to protect 6-digit workshop PIN codes and event slugs against distributed botnet brute-forcing and denial-of-service. - Dependencies:
- Valkey 8 for atomic replay cache (
SET ... NX EX 60). - Standard WebCrypto APIs supported across modern browsers and Deno.
- Valkey 8 for atomic replay cache (
- Additional Important Notes:
- PoW computation must be entirely transparent to human users (solved in background Web Workers or async microtasks during page load/typing).
- Headless CLI clients (
curl ... | source) must be provided with a streamlined solver or signed temporary ingress token.
2. Architectural Considerations & Risks
Risks
- Client-Side CPU Exhaustion & Mobile Battery Drain: If the cryptographic
difficulty target is set too high, mobile devices or older laptops might
freeze the main thread or experience noticeable latency before submitting a
PIN.
- Mitigation: Calibrate the default baseline difficulty to ~16–18 leading
zero bits (~20–40ms CPU time on standard mobile silicon). Run computation
inside a Web Worker or async
requestAnimationFramechunks to guarantee zero UI frame drops.
- Mitigation: Calibrate the default baseline difficulty to ~16–18 leading
zero bits (~20–40ms CPU time on standard mobile silicon). Run computation
inside a Web Worker or async
- Replay Attacks on Solved Nonces: An attacker could precompute a single
valid PoW solution and reuse it across thousands of simultaneous requests to
brute-force the 6-digit PIN.
- Mitigation: Bind challenges cryptographically to a short-lived
timestamped seed
(
HMAC-SHA256(secret, client_ip + timestamp_minute + event_id)). Record solved nonces in Valkey with a strict 60-second TTL viaSET key nonce NX EX 60to enforce single-use execution.
- Mitigation: Bind challenges cryptographically to a short-lived
timestamped seed
(
- Dynamic Threat Scaling: Static difficulty is either too easy for large
botnets or too heavy for low-powered mobile devices.
- Mitigation: Implement adaptive difficulty scaling in
server/pow.ts: Normal traffic requires 16 bits; if an IP or specific event encounters>3failed attempts within 60s, automatically scale difficulty to 22 bits for that subnet/event.
- Mitigation: Implement adaptive difficulty scaling in
Alternatives
- Cloudflare Turnstile / Google reCAPTCHA: Heavy external dependency requiring network egress, proprietary script tags, and tracking cookies. Rejected. A self-contained, zero-dependency SHA-256 PoW preserves Auth-Yes's 100% sovereign, zero-external-dependency IAM architecture.
- Strict IP Rate Limiting Only: In distributed botnet attacks, attackers rotate thousands of residential proxies, making single-IP rate limiting ineffective. PoW shifts the economic and compute cost onto the attacker.
3. Proposed Implementation
Phase 1: PoW Challenge Engine (server/pow.ts)
- Implement challenge minting:
generateChallenge(clientIp: string, eventId?: string, difficultyBits = 16):- Mint challenge payload:
{ salt: string, timestamp: number, difficulty: number, signature: string }. - Sign payload using internal HMAC key.
- Mint challenge payload:
- Implement validation:
verifyProofOfWork(challenge: PoWChallenge, nonce: number, clientIp: string):- Verify HMAC signature on salt/timestamp (ensure timestamp within 120s window).
- Compute
SHA-256(salt + nonce). - Check that the leading
Nbits of the digest are zero. - Atomically store
noncein Valkey (SET pow:${salt}:${nonce} 1 NX EX 120); if key already exists, reject as replayed.
Phase 2: Ingress Route Protection (server/routes/events.ts)
- Add challenge endpoint:
GET /api/join/challenge: Returns{ salt, timestamp, difficulty, signature }.
- Update
POST /api/join:- Require
pow_salt,pow_nonce,pow_timestamp,pow_signaturein JSON body. - Validate PoW before checking the PIN code or touching PostgreSQL.
- If invalid or missing, immediately return 400
(
Bad Request: Invalid Proof-of-Work Challenge) without consuming DB connections.
- Require
- Adaptive Difficulty Escalation:
- Track failed attempts per IP and per event in Valkey. Increase
difficultydynamically if failure rate spikes.
- Track failed attempts per IP and per event in Valkey. Increase
Phase 3: Client-Side WebCrypto Solver (ui/components/events/JoinScript.tsx)
- Background Web Worker Solver:
- When the user lands on
/joinor/e/:slug, automatically fetch/api/join/challengein the background. - Spawn a lightweight Web Worker executing WebCrypto
crypto.subtle.digest('SHA-256', ...)in a tight loop to find the nonce while the user types their 6-digit PIN. - Attach solved nonce payload seamlessly to the form submit handler.
- When the user lands on
- Headless CLI Compatibility:
- For CLI joins (
/join/:slug?format=env), embed a micro-solver script in the piped shell payload or provide a signed 1-click token for terminal workflows.
- For CLI joins (
Phase 4: Quality Gates & Benchmarks
- Comprehensive test suite in
server/tests/pow.test.ts:- Unit tests for challenge generation, difficulty checks, expiration, and replay prevention.
- Performance benchmark asserting solver verification on server takes
<0.5\text{ms}.
- Run
deno fmt,deno task lint,deno task check, anddeno test -A --no-check.