Description
hashAPIKey computes sha256.Sum256([]byte(key)) with no per-key salt, then stores the base64-encoded digest in the plaintext apikeys.key column. Because the hash is deterministic and unsalted, a database compromise (SQLi, backup leak, insider access) lets an attacker crack all stored keys offline via rainbow tables or brute force, cracking one key is as cheap as cracking any of them. Recovered keys can then be used to authenticate as the original holder against the gateway API and downstream agent pods.
Data flow
User-supplied API key (HTTP Basic Auth password or X-API-Key header) → hashAPIKey() → SHA-256 hash without salt → stored in apikeys.key column → looked up via GatewayGetAPIKeyByHash SQL query.
Reachability
Requires prior database read access, not directly network-exploitable. Once the DB is accessed, though, every stored key can be cracked offline with no per-record computation barrier, since there's no salt. The apikeys table also stores config_id, reference_id (org ID), prefix, and name, which helps an attacker triage high-value keys.
Impact
Full API key recovery on DB compromise, enabling impersonation of any user/workflow holding a key.
Suggested fix (backward-compatible, single PR)
Switch to HMAC-SHA-256 with a server-side pepper stored outside the DB (config/K8s secret):
func hashAPIKey(key string) string {
mac := hmac.New(sha256.New, pepperKey) // pepperKey from config/K8s secret
mac.Write([]byte(key))
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
This keeps the existing text column and lookup query unchanged, so it's a drop-in replacement.
A stronger option is Argon2id with a per-key salt, but that requires a DB migration and a rehash-on-next-use (or dual-read) strategy.
Description
hashAPIKeycomputessha256.Sum256([]byte(key))with no per-key salt, then stores the base64-encoded digest in the plaintextapikeys.keycolumn. Because the hash is deterministic and unsalted, a database compromise (SQLi, backup leak, insider access) lets an attacker crack all stored keys offline via rainbow tables or brute force, cracking one key is as cheap as cracking any of them. Recovered keys can then be used to authenticate as the original holder against the gateway API and downstream agent pods.Data flow
User-supplied API key (HTTP Basic Auth password or
X-API-Keyheader) →hashAPIKey()→ SHA-256 hash without salt → stored inapikeys.keycolumn → looked up viaGatewayGetAPIKeyByHashSQL query.Reachability
Requires prior database read access, not directly network-exploitable. Once the DB is accessed, though, every stored key can be cracked offline with no per-record computation barrier, since there's no salt. The
apikeystable also storesconfig_id,reference_id(org ID),prefix, andname, which helps an attacker triage high-value keys.Impact
Full API key recovery on DB compromise, enabling impersonation of any user/workflow holding a key.
Suggested fix (backward-compatible, single PR)
Switch to HMAC-SHA-256 with a server-side pepper stored outside the DB (config/K8s secret):
This keeps the existing text column and lookup query unchanged, so it's a drop-in replacement.
A stronger option is Argon2id with a per-key salt, but that requires a DB migration and a rehash-on-next-use (or dual-read) strategy.