Skip to content

Signature credentials are replayable and request-unbound #105

Description

@tnull

Summary

SignatureValidatingAuthorizer accepts a signature over only
SIGNING_CONSTANT || pubkey || unix_time. The proof is not bound to the HTTP
method, path, body, store_id, or a server nonce, and remains valid for up to
24 hours in either direction.

As a result, a captured Authorization header acts as a replayable bearer
credential for every operation belonging to that public key.

Observed on 88a5703496386465556d920dccf49512296c53d0 (current main).

Impact

Anyone who obtains one valid header can replay it to read, modify, or delete
the victim tenant's VSS state during the validity window. Header capture is
especially plausible when the service is exposed directly over its cleartext
HTTP listener.

Code evidence

  • auth-impls/src/signature.rs:64-86 checks only the timestamp and an ECDSA
    signature over the constant, public key, and timestamp.
  • verify receives only a header map, so it cannot bind the proof to request
    method, path, or body.
  • The resulting user_token is the public key hex string.

Proof of concept

Using the existing build_token helper in auth-impls/src/signature.rs, this
minimal test demonstrates that the same credential is accepted repeatedly:

let now = SystemTime::now()
    .duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
let (token, _) = build_token(now);
let mut headers = HashMap::new();
headers.insert("authorization".to_string(), token);

let auth = SignatureValidatingAuthorizer;
assert!(auth.verify(&headers).await.is_ok());
assert!(auth.verify(&headers).await.is_ok());

Because no request data is passed to verify, the two accepted uses can be
for different VSS routes and bodies.

Suggested remediation

  • Bind signatures to the method, canonical path, body hash, and tenant/store
    context.
  • Add a server-issued, single-use nonce with a short TTL.
  • Reduce the validity window and require TLS for authenticated traffic.

Reported by Bitcoin Red Team.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions