Summary
Description
In server.js (lines 52–55), the HMAC key used to derive credential-cache entries is computed as:
const _hmacKey = crypto
.createHash("sha256")
.update("pdf-qa-cred-cache") // hardcoded, public string
.digest();
This key is a deterministic SHA-256 hash of a string that is now publicly visible in the repository. The credential cache (_credCache) maps a 16-hex-character HMAC of ${sessionId}:${sessionSecret} to a { validatedAt } entry. Because the HMAC key is derived from a known constant, an attacker who knows any valid session_id (which is returned in API responses) can pre-compute the 16-character cache key for arbitrary sessionSecret values and determine whether a given (session_id, session_secret) pair is cached — a timing-oracle side-channel — without supplying the secret to the RAG service.
Steps to reproduce
Replace the static string derivation with a secret loaded from an environment variable (e.g., CRED_CACHE_HMAC_SECRET). If absent at startup, derive the key via crypto.randomBytes(32) (runtime-only — acceptable since the cache is in-memory and non-persistent). Document the env var in .env.example. Fail fast with a warning log if neither is provided in production.
Expected behavior
More concretely: by scanning the cache key space for the 16-hex-char output, an attacker with local access to the running process (e.g., a shared hosting environment, a container escape) can enumerate cached session secrets offline.
Actual behavior
- Offline brute-force of cached session credentials is feasible because the HMAC key is constant and known.
- The 16-character truncation of the HMAC output (64 bits) provides only ~2^64 theoretical key space, but pre-computation tables can be built for likely secret formats.
Affected Files
server.js lines 52–62 (_hmacKey derivation and _credKey function)
Additional context
No response
Summary
Description
In
server.js(lines 52–55), the HMAC key used to derive credential-cache entries is computed as:This key is a deterministic SHA-256 hash of a string that is now publicly visible in the repository. The credential cache (
_credCache) maps a 16-hex-character HMAC of${sessionId}:${sessionSecret}to a{ validatedAt }entry. Because the HMAC key is derived from a known constant, an attacker who knows any validsession_id(which is returned in API responses) can pre-compute the 16-character cache key for arbitrarysessionSecretvalues and determine whether a given(session_id, session_secret)pair is cached — a timing-oracle side-channel — without supplying the secret to the RAG service.Steps to reproduce
Replace the static string derivation with a secret loaded from an environment variable (e.g.,
CRED_CACHE_HMAC_SECRET). If absent at startup, derive the key viacrypto.randomBytes(32)(runtime-only — acceptable since the cache is in-memory and non-persistent). Document the env var in.env.example. Fail fast with a warning log if neither is provided in production.Expected behavior
More concretely: by scanning the cache key space for the 16-hex-char output, an attacker with local access to the running process (e.g., a shared hosting environment, a container escape) can enumerate cached session secrets offline.
Actual behavior
Affected Files
server.jslines 52–62 (_hmacKeyderivation and_credKeyfunction)Additional context
No response