Skip to content

security fix(gateway): defend pre-auth device-signature verify against CPU DoS#77492

Open
fede-kamel wants to merge 1 commit into
openclaw:mainfrom
fede-kamel:security/preauth-signature-rate-limit
Open

security fix(gateway): defend pre-auth device-signature verify against CPU DoS#77492
fede-kamel wants to merge 1 commit into
openclaw:mainfrom
fede-kamel:security/preauth-signature-rate-limit

Conversation

@fede-kamel

@fede-kamel fede-kamel commented May 4, 2026

Copy link
Copy Markdown
Contributor

Closes #77979

Summary

  • Pre-auth WS handshakes that include a device block ran crypto.createPublicKey plus a v3-then-v2 crypto.verify per request, gated only by frame-size limits. An unauthenticated remote attacker could pin a single Gateway core: PoC against a real running gateway hits ~510 forged handshakes/s/IP, degrading legit handshake p50 by 48.7× (1.16 ms → 56.6 ms) and p99 by 94.7×.
  • Fix is layered: schema cap on device.publicKey (1024) and device.signature (256); shape pre-check on verifyDeviceSignature / deriveDeviceIdFromPublicKey / normalizeDevicePublicKeyBase64Url rejecting inputs that cannot decode to a 32-byte raw key or 64-byte signature; new AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE bucket gating the verify per IP. The browser-origin rate limiter is always constructed (server.impl.ts:424), so the gate fires for browser-origin clients even when no gateway.auth.rateLimit is configured. For non-browser-origin clients, the gate fires when the operator configures a rate limiter (the typical production case).
  • Loopback exemption is preserved so legit local CLI sessions are never throttled; the gate fires through the non-loopback-exempt limiter for remote and browser-origin clients, which is the production threat path.

Reproduction

The PoC (scripts/poc-preauth-flood-ws.mjs) opens N concurrent WS connections, each completing the connect-challenge handshake with a real Ed25519 PEM public key and a random 64-byte signature. The gateway reaches the verify path (server-issued nonce echoed back, device.id matches sha256(rawPublicKey)) and runs createPublicKey + v3 verify + v2 verify per request. A separate measurement client probes time-to-first-event in parallel.

Against a vulnerable gateway:

pnpm gateway:watch                                    # in another terminal

node scripts/poc-preauth-flood-ws.mjs \
     --gateway ws://127.0.0.1:18789 \
     --attackers 32 \
     --legit-iters 30 \
     --duration-ms 3000

Observed:

baseline:     n=30 p50=1.16ms  p90=1.80ms  p99=2.04ms
under-attack: n=30 p50=56.58ms p90=64.24ms p99=193.36ms
attacker totals: connections=1531  rate=510/s   (one IP, one node process)
legit handshake p50 ratio (under-attack / baseline): 48.69x
legit handshake p99 ratio: 94.73x

To validate the fix end-to-end without exposing a dev gateway to the LAN, the in-process integration test runs the same forged-handshake sequence against a real withGatewayServer instance configured with rateLimit.maxAttempts: 1, exemptLoopback: true:

pnpm test src/gateway/server.preauth-signature-rate-limit.test.ts

The test asserts:

  1. The first N forged signatures fail with error.details.reason === "device-signature" (the verify ran).
  2. The (N+1)th attempt fails with error.details.reason === "device-signature-rate-limited" and retryAfterMs > 0 — the gate short-circuited before any crypto.

Reverting the recordFailure line in the rate-limit gate makes both tests fail with expected 'device-signature' to be 'device-signature-rate-limited'.

Test plan

  • src/gateway/server.preauth-signature-rate-limit.test.ts — new in-process WS integration test (2 cases). Both fail when the fix is disabled.
  • src/infra/device-identity.test.ts — new shape pre-check tests: oversized PEM, oversized non-PEM, signature-shaped input rejected as public key, public-key-shaped input rejected as signature, all without invoking crypto.
  • src/gateway/protocol/connect-device-bounds.test.ts — new schema-cap tests: validateConnectParams rejects oversized device.publicKey and device.signature; accepts them at the documented bounds.
  • Existing src/gateway/server/ws-connection/handshake-auth-helpers.test.ts — 27/27 pass, no regression.
  • Live PoC reproduction confirms the 50× p50 / 95× p99 degradation pre-fix.

Verification

  • Targeted pnpm test of the four affected files: 41/41 pass.
  • Build: dist contains the new rate-limit gate (grep AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE on the bundle).
  • Type errors observed during local pnpm tsgo are pre-existing and unrelated (pi-embedded-runner.* AgentMessage casts and missing web-tree-sitter dep).

Real behavior proof

  • Behavior addressed: pre-auth device blocks ran crypto.createPublicKey plus a v3-then-v2 crypto.verify per request with no per-IP cap. An unauthenticated attacker could pin a Gateway core (~510 forged handshakes/s/IP, 48.7× p50 / 94.7× p99 degradation against legitimate handshakes per the network-level PoC numbers above). The fix layers three defenses: ajv schema caps reject oversized device.publicKey and device.signature before any handler runs; isPlausibleDevicePublicKeyInput / isPlausibleDeviceSignatureInput shape pre-checks reject inputs that cannot decode to a 32-byte raw key or 64-byte signature without invoking crypto; and AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE gates the verify per IP, so once the bucket is exhausted no createPublicKey or verify runs.
  • Real environment tested: local Node v22.22.2 runtime against the real patched modules from this branch (security/preauth-signature-rate-limit HEAD 6233fc0e60). No vitest, no mocks. The reproducer imports validateConnectParams from the patched protocol/index.ts, the cap constants and isPlausibleDevice* helpers from infra/device-identity.ts, and createAuthRateLimiter from gateway/auth-rate-limit.ts, then exercises each layer against representative attacker payloads.
  • Exact steps or command run after this patch: a node runtime tsx reproducer at https://gist.github.com/fede-kamel/974f456b4269b7242e1c91b9dc46c178 runs three layers — schema caps against oversized device.publicKey/device.signature (including a 60 KB representative attacker payload), shape pre-checks against malformed inputs that decode to wrong-length raw bytes, and the AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE bucket fired against a single attacker IP. Run as node --import tsx <local-copy>.mts from a worktree on this branch.
  • Evidence after fix: live terminal output captured from the node runtime against the patched modules:
Pre-auth device-signature CPU-DoS proof (PR 77492)
Imports real patched validateConnectParams + isPlausibleDevice* + AuthRateLimiter
from security/preauth-signature-rate-limit worktree.

MAX_DEVICE_PUBLIC_KEY_INPUT_CHARS = 1024
MAX_DEVICE_SIGNATURE_INPUT_CHARS  = 256
ED25519_RAW_PUBLIC_KEY_BYTES      = 32
ED25519_SIGNATURE_BYTES           = 64

=== Layer 1: schema-level rejection of oversized device.publicKey / device.signature ===

case                                                          | result | took
--------------------------------------------------------------|--------|------
valid: device.publicKey real PEM (113 chars)                  | PASS   | 4.494ms
reject: device.publicKey at 1025 chars (cap+1)                | REJECT | 0.515ms
reject: device.publicKey at 60KB (representative attacker payload)| REJECT | 4.246ms
reject: device.signature at 257 chars (cap+1)                 | REJECT | 0.077ms

=== Layer 2: shape pre-check on device-identity helpers (no crypto) ===

case                                                          | result | took
--------------------------------------------------------------|--------|------
valid: real raw 32-byte base64url public key                  | PASS   | 0.486ms
valid: real PEM public key                                    | PASS   | 0.039ms
reject: random 100-byte base64                                | REJECT | 0.629ms
reject: oversized publicKey (cap+1)                           | REJECT | 0.053ms
reject: empty publicKey                                       | REJECT | 0.048ms
valid: real 64-byte base64url signature                       | PASS   | 0.101ms
reject: random 32-byte signature input                        | REJECT | 0.118ms
reject: oversized signature (cap+1)                           | REJECT | 0.035ms
reject: 60KB signature                                        | REJECT | 0.044ms

=== Layer 3: rate-limit gate AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE ===

attempt | rateLimited | remaining/retryAfterMs | crypto entered?
--------|-------------|------------------------|------------------
    1   | false       | remaining=3            | would enter
    2   | false       | remaining=2            | would enter
    3   | false       | remaining=1            | would enter
    4   | true        | retryAfterMs=60000     | skipped
    5   | true        | retryAfterMs=59999     | skipped
    6   | true        | retryAfterMs=59999     | skipped
    7   | true        | retryAfterMs=59999     | skipped
    8   | true        | retryAfterMs=59999     | skipped

=== Layer 3b: cost comparison — skipped vs entered ===

crypto entered 5000 times: 214.3ms (avg 0.043ms/op)
gate-blocked  5000 times: 62.6ms (avg 0.013ms/op)
amplification factor under attack: 3.4x
  • Observed result after fix: every legitimate input validated PASS and every cap+1 / 60 KB / wrong-length / wrong-shape attacker input validated REJECT across both schema (Layer 1) and shape pre-check (Layer 2). The rate-limit gate (Layer 3) blocked attempts 4 through 8 from a single attacker IP after the configured maxAttempts: 3, never reaching createPublicKey/verify. Layer 3b measured the per-op cost of the full Ed25519 verify path (0.043 ms/op) versus the gate-blocked path (0.013 ms/op) — a 3.4× per-op CPU saving on every attempt past the threshold, which compounds against the 510/s/IP attacker rate observed in the network-level PoC.
  • What was not tested: the WS upgrade and connect-challenge wire protocol are not driven by this reproducer; that integration shape is covered by server.preauth-signature-rate-limit.test.ts (in-process WS server) and by the live scripts/poc-preauth-flood-ws.mjs PoC against a running pnpm gateway:watch. Concurrent multi-IP attacker behavior was not measured in this reproducer; the gate is per-IP and isolation matches the existing device-token bucket.

Notes for reviewer

  • This branch is also flagged triage: dirty-candidate because the diff touches schema, device-identity helpers, and the WS message handler. The three layers are intentionally tightly coupled — the schema cap is a cheap fast-reject, the shape pre-check covers in-spec but malformed inputs that pass the schema, and the rate-limit gate covers attackers presenting in-shape Ed25519 keypairs (their own). Splitting the layers would either leave one layer behind a partial defense or duplicate the fix surface across PRs. Happy to split if reviewers prefer one layer per PR.
  • The recordFailure is intentionally pessimistic before crypto runs — an attacker with their own keypair producing valid self-signatures should still consume the bucket. reset only fires after resolveConnectAuthDecision confirms the full handshake is authorized.

@fede-kamel fede-kamel requested a review from a team as a code owner May 4, 2026 19:27
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime scripts Repository scripts size: L triage: dirty-candidate Candidate: broad unrelated surfaces; may need splitting or cleanup. labels May 4, 2026
@clawsweeper

clawsweeper Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 12, 2026, 5:15 PM ET / 21:15 UTC.

Summary
Adds gateway connect-field bounds, Ed25519 input-shape checks, an IP-scoped pre-auth device-signature limiter, focused gateway tests, and a WebSocket flood PoC.

PR surface: Source +152, Tests +452, Other +349. Total +953 across 11 files.

Reproducibility: yes. Current main’s unauthenticated device path reaches Ed25519 parsing and verification, and the supplied PoC provides a concrete running-gateway flood with measured latency degradation; this read-only review did not rerun destructive load.

Review metrics: 2 noteworthy metrics.

  • Protocol limits: 2 connect fields newly capped. Existing clients outside the new public-key or signature bounds will be rejected during schema validation.
  • Concurrency guard: 1 new limiter scope, 0 signature-path serializer calls. The expensive device-signature path can still admit concurrent requests beyond the configured threshold.

Merge readiness
Overall: 🦪 silver shellfish
Proof: 🦪 silver shellfish
Patch quality: 🦪 silver shellfish
Result: blocked until stronger real behavior proof is added.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • [P1] Serialize the full signature attempt and add a concurrent forged-burst regression.
  • [P1] Post redacted current-head running-gateway WebSocket after-fix output.

Proof guidance:

  • [P1] Needs stronger real behavior proof before merge: The body includes strong pre-fix running-gateway measurements and after-fix module output against old SHA 6233fc0, but no current-head running-gateway concurrent proof for SHA 6a6a120; add redacted terminal output or logs, update the PR body to trigger review, and ask a maintainer for @clawsweeper re-review if needed.

Risk before merge

  • [P1] Concurrent forged connections can exceed maxAttempts before limiter state is recorded, leaving the availability vulnerability partially open.
  • [P1] The two new protocol bounds and earlier IP-wide lockout can reject previously accepted clients or affect multiple legitimate clients sharing an address.
  • [P1] A successful authorized connection resets the IP-wide signature bucket, so shared-IP reset ownership needs explicit coverage and gateway security approval.
  • [P1] The after-fix evidence does not demonstrate current head resisting the described concurrent flood through a running gateway WebSocket handler.

Maintainer options:

  1. Serialize the complete signature attempt (recommended)
    Use the existing per-IP/per-scope serializer around admission, crypto, authorization, and accounting, then add concurrent regression coverage before merge.
  2. Define narrower bucket ownership
    If an IP-wide successful reset is unsafe for shared clients, choose a narrower key or reset policy with explicit gateway security-owner approval.
  3. Pause for security policy review
    Pause if maintainers cannot approve the protocol caps, shared-IP lockout behavior, and reset semantics as one coherent security policy.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Use the existing per-IP/per-scope serialized rate-limit attempt helper for the full device-signature admission and accounting path; add concurrent forged-handshake coverage proving crypto entries do not exceed maxAttempts and explicit shared-IP success/reset coverage; preserve existing fallback and error-detail contracts; do not edit CHANGELOG.md.

Next step before merge

  • [P1] The blocking concurrency defect has a narrow mechanical repair using an existing repository serializer; contributor-owned current-head real proof and security-owner approval remain required after repair.

Security
Needs attention: The input hardening is security-positive, but non-atomic limiter admission leaves a concrete concurrent bypass of the intended DoS defense.

Review findings

  • [P1] Serialize device-signature rate-limit attempts — src/gateway/server/ws-connection/message-handler.ts:975-1004
Review details

Best possible solution:

Wrap the complete device-signature admission, crypto verification, authorization result, and limiter accounting in the existing keyed serializer; add concurrent forged-burst and shared-IP reset coverage; then provide current-head running-gateway proof.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main’s unauthenticated device path reaches Ed25519 parsing and verification, and the supplied PoC provides a concrete running-gateway flood with measured latency degradation; this read-only review did not rerun destructive load.

Is this the best way to solve the issue?

No. The layered bounds and shape checks are useful, but the limiter must reuse the repository’s atomic keyed serializer across the entire expensive attempt before this is the best fix.

Full review comments:

  • [P1] Serialize device-signature rate-limit attempts — src/gateway/server/ws-connection/message-handler.ts:975-1004
    Wrap the full device-signature admission, crypto work, authorization decision, and limiter accounting in withSerializedRateLimitAttempt. As written, concurrent handshakes can all pass check() before any handler records its failure, so substantially more than maxAttempts requests still enter createPublicKey/verify; add a concurrent forged-burst regression that counts crypto entries.
    Confidence: 0.99

Overall correctness: patch is incorrect
Overall confidence: 0.99

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 8d9ce35b92c4.

Label changes

Label justifications:

  • P1: The PR targets a remotely triggerable pre-auth CPU denial-of-service, but the current patch leaves a concurrent bypass of the intended attempt budget.
  • merge-risk: 🚨 compatibility: Two connect-frame fields gain maximum lengths and an earlier IP-wide lockout can change behavior for existing or shared-address clients.
  • merge-risk: 🚨 security-boundary: The patch changes unauthenticated cryptographic admission and rate-limit failure/reset accounting.
  • merge-risk: 🚨 availability: The concurrent race can leave the DoS partially open, while broad lockout/reset behavior can affect legitimate gateway availability.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The body includes strong pre-fix running-gateway measurements and after-fix module output against old SHA 6233fc0, but no current-head running-gateway concurrent proof for SHA 6a6a120; add redacted terminal output or logs, update the PR body to trigger review, and ask a maintainer for @clawsweeper re-review if needed.
Evidence reviewed

PR surface:

Source +152, Tests +452, Other +349. Total +953 across 11 files.

View PR surface stats
Area Files Added Removed Net
Source 6 161 9 +152
Tests 4 455 3 +452
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 349 0 +349
Total 11 965 12 +953

Security concerns:

  • [high] Concurrent admission bypasses the crypto budget — src/gateway/server/ws-connection/message-handler.ts:975
    Multiple unauthenticated handlers can pass the limiter check before any records an attempt, allowing an attacker to exceed maxAttempts expensive signature operations during a burst.
    Confidence: 0.99

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs src/gateway/server.preauth-signature-rate-limit.test.ts.
  • [P1] node scripts/run-vitest.mjs src/gateway/server/ws-connection/auth-context.test.ts.
  • [P1] node scripts/run-vitest.mjs src/gateway/auth-rate-limit.test.ts.
  • [P1] node scripts/crabbox-wrapper.mjs run -- env OPENCLAW_CHECK_CHANGED_REMOTE_CHILD=1 OPENCLAW_CHANGED_LANES_RAW_SYNC=1 corepack pnpm check:changed.

What I checked:

Likely related people:

  • Shakker: Introduced the current keyed rate-limit attempt serializer and integrated it into gateway authentication and pairing paths on current main. (role: recent area contributor; confidence: high; commits: 34a1102506d8; files: src/gateway/rate-limit-attempt-serialization.ts, src/gateway/server/ws-connection/auth-context.ts, src/gateway/server/ws-connection/message-handler.ts)
  • fede-kamel: Authored the merged bootstrap-token DoS defense that established the sibling serialized pre-auth verification pattern now relevant to this PR. (role: introduced sibling behavior; confidence: high; commits: ecbd97e9682d; files: src/gateway/server/ws-connection/auth-context.ts, src/gateway/server.preauth-bootstrap-token-rate-limit.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch 4 times, most recently from 3bf503e to 4a844cf Compare May 4, 2026 19:59
@fede-kamel

fede-kamel commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressing the two Codex findings on 4a844cf (force-pushed):

1. Limiter now runs before any pre-auth crypto (message-handler.ts:754-781)

Hoisted the authRateLimiter.check(..., AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE) gate above deriveDeviceIdFromPublicKey(). A locked-out IP no longer pays the crypto.createPublicKey() cost via PEM input — the gate fires first regardless of public-key encoding.

2. Bucket reset deferred until after resolveConnectAuthDecision (message-handler.ts:884-886)

Two changes:

  • After the limiter check passes, recordFailure is now called pessimistically before crypto runs. Every device-bearing handshake that crosses the gate consumes the bucket up-front.
  • reset() only fires after resolveConnectAuthDecision returns authOk === true. The intermediate recordFailure on payloadVersion === null was removed since the pessimistic call already counts that attempt.

Net behavior: an attacker with their own keypair producing a valid self-signature still consumes the bucket because resolveConnectAuthDecision rejects them as unauthorized — the reset never fires, the pessimistic recordFailure stays on the books.

Coverage added in server.preauth-signature-rate-limit.test.ts:

Test results:

pnpm test src/gateway/server.preauth-signature-rate-limit.test.ts \
          src/infra/device-identity.test.ts \
          src/gateway/protocol/connect-device-bounds.test.ts \
          src/gateway/server/ws-connection/handshake-auth-helpers.test.ts \
          src/gateway/server.auth.browser-hardening.test.ts \
          src/gateway/server/ws-connection/auth-context.test.ts
# 6 files, 64 tests, all pass

Two pre-existing assertions in server.auth.browser-hardening.test.ts were updated to accept either "retry later" (shared-secret bucket) or "rate-limited" (device-signature bucket) since both are valid loopback-browser-origin lockout signals — with the gate moved earlier and the pessimistic recordFailure, the device-signature bucket now wins the race in those tests at maxAttempts: 1.

Re-review progress:

@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from 4a844cf to 49334e7 Compare May 4, 2026 20:07
@fede-kamel fede-kamel changed the title fix(gateway): defend pre-auth device-signature verify against CPU DoS security fix(gateway): defend pre-auth device-signature verify against CPU DoS May 4, 2026
@fede-kamel

Copy link
Copy Markdown
Contributor Author

@steipete when you have a moment — security fix for pre-auth device-signature CPU DoS, CI green. Will rebase to clear the conflicts before merge. Thanks!

@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from 4b6b34a to 3166348 Compare May 5, 2026 16:30
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 5, 2026
@fede-kamel

Copy link
Copy Markdown
Contributor Author

cc @openclaw/openclaw-secops for visibility — rebased onto main, mergeable. Just routing through the right team. No rush. Thanks!

@fede-kamel

Copy link
Copy Markdown
Contributor Author

Rebased onto current origin/main (was 372 commits behind). The previous "Real behavior proof" CI failure was a missing-script artifact — scripts/github/real-behavior-proof-check.mjs was added in #77622 after this branch was first pushed, so the workflow could not load on the old base. The script is on HEAD now and CI is re-running.

@openclaw/openclaw-secops — flagging for review (pre-auth device-signature CPU-DoS hardening).

@openclaw-barnacle openclaw-barnacle Bot added proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels May 9, 2026
@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from 592b198 to 6233fc0 Compare May 9, 2026 17:58
@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from 6233fc0 to facb002 Compare May 9, 2026 18:39
@jesse-merhi jesse-merhi added security Security documentation and removed security Security documentation labels May 25, 2026
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels May 25, 2026
@clawsweeper

clawsweeper Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from facb002 to 1fc8137 Compare May 30, 2026 14:51
@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch 2 times, most recently from 6d0036f to d3ca8cf Compare June 12, 2026 15:37
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. label Jun 12, 2026
…lification DoS

Pre-auth handshakes that include a `device` block previously ran
`crypto.createPublicKey` plus a v3-then-v2 `crypto.verify` per request,
gated only by frame-size limits — an unauthenticated remote attacker
could pin a single Gateway core (~500 forged handshakes/s/IP measured
in PoC, 50x p50 / 95x p99 legit handshake degradation).

Layered defense:
- Schema: cap device.publicKey at 1024 chars and device.signature at 256
  chars (every valid Ed25519 form fits with margin) so oversized strings
  never reach the crypto path.
- Pre-check: short-circuit verifyDeviceSignature, deriveDeviceIdFromPublicKey,
  and normalizeDevicePublicKeyBase64Url on inputs that cannot decode to a
  32-byte raw key or 64-byte signature.
- Rate limit: gate the verify behind a new AUTH_RATE_LIMIT_SCOPE_DEVICE_SIGNATURE
  bucket (per-IP, fires through the always-constructed browser-origin
  limiter and through the regular limiter when configured).

In-process integration test exercises the full WS handshake and proves
the rate-limit gate truncates attacker work after maxAttempts; reverting
the gate makes the test fail.
@fede-kamel fede-kamel force-pushed the security/preauth-signature-rate-limit branch from d3ca8cf to 6a6a120 Compare June 12, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui gateway Gateway runtime merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. scripts Repository scripts size: L status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. triage: dirty-candidate Candidate: broad unrelated surfaces; may need splitting or cleanup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: pre-auth device-signature verify allows CPU-amplification DoS without rate limit

2 participants