Skip to content

Comments

fix: Use constant-time comparison for token lookup#106

Merged
jasonalmaturner merged 1 commit intomasterfrom
jat/secure-compare-token-lookup
Feb 19, 2026
Merged

fix: Use constant-time comparison for token lookup#106
jasonalmaturner merged 1 commit intomasterfrom
jat/secure-compare-token-lookup

Conversation

@jasonalmaturner
Copy link
Contributor

Summary

Replace direct map key lookup (token_map[val]) with Plug.Crypto.secure_compare/2 to prevent timing attacks on token matching.

Standard map lookup uses equality comparison that short-circuits on the first mismatched byte, which can leak information about how many prefix characters matched via response time differences. secure_compare always compares every byte in constant time.

Changes

  • Added secure_lookup/2 private function that iterates the token map using Plug.Crypto.secure_compare/2
  • Replaced token_map[val] with secure_lookup(token_map, val) in call/2
  • No new dependencies — Plug.Crypto is already available via the existing :plug dependency

Test plan

  1. All 19 existing tests pass unchanged
  2. Behaviorally identical — same inputs produce same outputs, just timing-safe

🤖 Generated with Claude Code

@jasonalmaturner jasonalmaturner requested a review from a team as a code owner February 17, 2026 20:20
Replace direct map key lookup with Plug.Crypto.secure_compare/2
to prevent timing attacks on token matching. The map lookup uses
standard equality which can leak information about how many
characters matched via response time differences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jasonalmaturner jasonalmaturner force-pushed the jat/secure-compare-token-lookup branch from c57dd69 to 6303838 Compare February 17, 2026 20:26
@jasonalmaturner
Copy link
Contributor Author

Test Results

Unit tests

All 19 existing tests pass unchanged:

mix test
Running ExUnit with seed: 337265, max_cases: 28
...................
Finished in 0.04 seconds
19 tests, 0 failures

Manual verification via mix run

Configured a test token and ran the plug against various inputs:

Application.put_env(:simple_token_authentication, :service_tokens, [test_svc: "my-secret-token"])

# Valid token
conn = Plug.Test.conn(:get, "/test") |> Plug.Conn.put_req_header("authorization", "my-secret-token")
result = SimpleTokenAuthentication.call(conn, [])
# → status: nil (pass-through), service: :test_svc

# Invalid token
conn = Plug.Test.conn(:get, "/test") |> Plug.Conn.put_req_header("authorization", "wrong-token")
result = SimpleTokenAuthentication.call(conn, [])
# → status: 401, service: nil

# No token
conn = Plug.Test.conn(:get, "/test")
result = SimpleTokenAuthentication.call(conn, [])
# → status: 401, service: nil
Test Status Service
Valid token nil (pass-through) :test_svc
Invalid token 401 nil
No token 401 nil

Behaviorally identical to the previous map lookup implementation.

@jasonalmaturner jasonalmaturner enabled auto-merge (squash) February 17, 2026 22:01
@jasonalmaturner jasonalmaturner merged commit 50f5883 into master Feb 19, 2026
9 checks passed
@jasonalmaturner jasonalmaturner deleted the jat/secure-compare-token-lookup branch February 19, 2026 16:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants