Skip to content

sec: constant-time admin token compare#294

Merged
asternic merged 2 commits into
asternic:mainfrom
Flow-Mind-Company:flow-mind-prod
May 6, 2026
Merged

sec: constant-time admin token compare#294
asternic merged 2 commits into
asternic:mainfrom
Flow-Mind-Company:flow-mind-prod

Conversation

@Flow-Mind-Company
Copy link
Copy Markdown

Summary

Direct equality check on the admin token (token != *adminToken) leaks information about the token byte-by-byte through a timing channel. Replaces it with subtle.ConstantTimeCompare.

Why

adminToken is the master credential for /admin/* endpoints — creating users, listing them, etc. Any user created via this endpoint can connect a WhatsApp instance, so a leaked admin token compromises every tenant on the deployment.

In a multi-tenant or hosted setup, a remote attacker with network access to the API can mount a timing oracle over many short HTTPS requests and recover the token character by character. subtle.ConstantTimeCompare is the standard mitigation — it takes the same time regardless of where the strings differ.

What changed

// before
if token != *adminToken {

// after
if subtle.ConstantTimeCompare([]byte(token), []byte(*adminToken)) != 1 {

3 lines (crypto/subtle import + the compare line + comment).

Backwards compatibility

100% — pure security hardening. Same input/output behaviour for all callers, no new env vars, no schema changes.

Verification

  • go build ./... passes
  • go vet ./... passes
  • curl -H 'Authorization: <correct>' → 200 (unchanged)
  • curl -H 'Authorization: <wrong>' → 401 (unchanged)

Happy to apply the same hardening to the per-user authalice token path if you'd like — that one currently hits the DB / cache, so the timing leak is much smaller, but I can rewrite it if you prefer defence-in-depth.

Direct == on admin token leaks length-bytes via timing channel.
Replace with subtle.ConstantTimeCompare. Pre-prod hardening for
multi-tenant deploy where admin token grants user-creation rights.
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request improves security by implementing constant-time comparison for admin token validation to mitigate timing attacks. The review feedback correctly identifies that the current implementation still leaks the token length and suggests comparing SHA-256 hashes of the tokens to eliminate this side-channel.

Comment thread handlers.go
import (
"bytes"
"context"
"crypto/subtle"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support a more robust constant-time comparison that avoids leaking the token length, the "crypto/sha256" package is required.

Suggested change
"crypto/subtle"
"crypto/sha256"
"crypto/subtle"

Comment thread handlers.go
token := r.Header.Get("Authorization")
if token != *adminToken {
// Constant-time compare to avoid timing-attack leak of admin token bytes.
if subtle.ConstantTimeCompare([]byte(token), []byte(*adminToken)) != 1 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

security-medium medium

While subtle.ConstantTimeCompare prevents byte-by-byte timing leaks, it still leaks the length of the adminToken because it returns immediately if the lengths of the two slices differ. A more robust approach is to compare the SHA-256 hashes of both tokens. Since hashes have a fixed length, this eliminates the length side-channel. For even better performance, the hash of the expected adminToken could be precomputed at startup.

Suggested change
if subtle.ConstantTimeCompare([]byte(token), []byte(*adminToken)) != 1 {
tokenHash := sha256.Sum256([]byte(token))
adminHash := sha256.Sum256([]byte(*adminToken))
if subtle.ConstantTimeCompare(tokenHash[:], adminHash[:]) != 1 {

@asternic asternic merged commit faa44e8 into asternic:main May 6, 2026
1 check passed
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.

2 participants