Static Salt for API Key Hashing
Every partner API key was hashed with a hardcoded checked-in salt, so a database dump alone was sufficient to mount a parallel brute-force against every API key hash. Fixed by adding a server-side pepper from GCP Secret Manager.
Description
// api-key.service.ts L12const apiKeyHashingSalt = "ipump"; // hardcoded, same for ALL keys// api-key.service.ts L36-L38public async getByKey(key: string): Promise<ApiKey> {const hashedKey = this.hashingService.hash(key, apiKeyHashingSalt); // static salt usedreturn this.apiKeyRepository.findByHash(hashedKey.hash);}
// hashing.service.ts L10-L18public hash(input: string, salt: string | undefined): HashResult {const iterations = 100000;const keyLength = 64;const digest = "sha512";const saltLength = 32;const saltOrRandomSalt = salt ?? randomBytes(saltLength).toString("hex");// When salt is provided (always "ipump"), no random salt is generatedconst hash = pbkdf2Sync(input, saltOrRandomSalt, iterations, keyLength, digest).toString("hex");return { hash, salt: saltOrRandomSalt };}
Frontend / on-chain mitigation analysis: Not applicable, purely backend cryptographic concern.
Impact
If the database is compromised, the attacker knows the salt for every API key hash, enabling parallel brute-force against all hashes simultaneously. High key entropy (64 hex chars) provides significant resistance, but the static salt weakens defense-in-depth.
Recommendation
Use the per-key salt already stored in the database.
Resolution
Fixed in PR #3687, added a server-side pepper from GCP Secret Manager (HMAC-SHA256(PBKDF2(plainKey, salt), pepper)); a database dump alone no longer compromises the hashes.

