Skip to content

Use constant-time comparison for token and credential verification#506

Open
prk-Jr wants to merge 4 commits intomainfrom
hardening/constant-time-comparisons
Open

Use constant-time comparison for token and credential verification#506
prk-Jr wants to merge 4 commits intomainfrom
hardening/constant-time-comparisons

Conversation

@prk-Jr
Copy link
Collaborator

@prk-Jr prk-Jr commented Mar 16, 2026

Summary

  • Replace == comparisons with subtle::ConstantTimeEq in all three secret-verification sites (tstoken, clear-URL signature, Basic Auth), eliminating timing side-channel attacks
  • Fix a secondary short-circuit oracle in Basic Auth: && allowed an attacker to distinguish wrong-username from wrong-password via timing — replaced with bitwise & on subtle::Choice so both fields are always evaluated
  • Add log::warn! on auth failure (path only, no credentials) and five new targeted tests covering each fix

Changes

File Change
Cargo.toml Add subtle = "2" to workspace dependencies
crates/common/Cargo.toml Add subtle as direct dependency
crates/common/src/auth.rs CT comparison with & instead of &&; log::warn! on failure; 2 new tests
crates/common/src/http_util.rs CT comparison in verify_clear_url_signature; 2 new tests
crates/common/src/proxy.rs CT comparison for tstoken in reconstruct_and_validate_signed_target; 1 new test

Closes

Closes #410

Test plan

  • cargo test --workspace
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo fmt --all -- --check
  • JS tests: cd crates/js/lib && npx vitest run
  • JS format: cd crates/js/lib && npm run format
  • Docs format: cd docs && npm run format
  • WASM build: cargo build --bin trusted-server-fastly --release --target wasm32-wasip1
  • Manual testing via fastly compute serve

JS tests are failing on main due to a pre-existing ESM/CJS incompatibility in html-encoding-sniffer node_modules — unrelated to this PR.

Checklist

  • Changes follow CLAUDE.md conventions
  • No unwrap() in production code — use expect("should ...")
  • Uses tracing macros (not println!)
  • New code has tests
  • No secrets or credentials committed

Replace standard == comparisons with subtle::ConstantTimeEq in the three
places that verify secrets: tstoken signature in proxy.rs, clear-URL
signature in http_util.rs, and Basic Auth credentials in auth.rs.

The auth fix also removes the && short-circuit that created a username-
existence oracle — both username and password are now always evaluated
using bitwise & on subtle::Choice values.

Adds log::warn on auth failure (path only, no credentials) and five
targeted tests covering tampered tokens, wrong-username-right-password,
and empty tokens.

Closes #410
@prk-Jr prk-Jr self-assigned this Mar 16, 2026
Copy link
Collaborator

@ChristianPavilonis ChristianPavilonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See below

Copy link
Collaborator

@ChristianPavilonis ChristianPavilonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Clean, well-focused security hardening PR. Ready to merge with minor improvements.

Excellent anti-oracle design in auth.rs — bitwise & on subtle::Choice instead of && prevents username-existence oracle. Minimal, focused changes — only touches the 3 comparison sites + targeted tests. Good test coverage (5 new tests). subtle v2 is the right dependency choice.

Copy link
Collaborator

@ChristianPavilonis ChristianPavilonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — ready to merge

This PR correctly hardens the three token/credential comparison sites with constant-time operations from the subtle crate. The changes are minimal, focused, and well-documented.

Highlights

  • Excellent anti-oracle design in auth.rs: using bitwise & on subtle::Choice instead of && prevents username-existence oracles. The inline comment explaining why is exactly what future maintainers need.
  • Well-chosen tests covering tampered tokens (same length, wrong bytes), wrong-username/right-password, correct-username/wrong-password, and empty tokens.
  • Good doc comments explaining security invariants on enforce_basic_auth, verify_clear_url_signature, and inline in proxy.rs.
  • log::warn! on auth failure logs path only (no credentials) — correct observability without leaking secrets.
  • Minimal, focused changes — only the 3 comparison sites plus targeted tests. No unnecessary refactoring.
  • All CI checks pass (fmt, test, clippy, vitest, CodeQL).

Minor note (not actionable for this PR)

  • extract_credentials in auth.rs (lines 49-73) has pre-existing early returns that leak timing about credential format (missing header, wrong scheme, malformed base64). This is not introduced by this PR and is fine — the CT comparison correctly protects credential values, not header format parsing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Non-constant-time token/password comparison enables timing attacks

3 participants