From c180eefca459799de220b1c99583d880bb1841f7 Mon Sep 17 00:00:00 2001 From: vuckro Date: Wed, 10 Jun 2026 11:51:43 +0200 Subject: [PATCH] fix(security): constant-time comparison for REST API credentials validate_credentials() compared the stored API key and secret with === , which short-circuits on the first differing byte and can leak the values through response timing. Compare with hash_equals() instead, and reject the 'prevent' sentinel (used before credentials are generated) and empty credentials explicitly so they can never authenticate. Co-Authored-By: Claude Fable 5 --- inc/class-api.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/inc/class-api.php b/inc/class-api.php index 9033876db..2ea0e8f57 100644 --- a/inc/class-api.php +++ b/inc/class-api.php @@ -324,7 +324,21 @@ public function get_auth() { */ public function validate_credentials($api_key, $api_secret) { $auth = $this->get_auth(); - return $api_key === $auth['api_key'] && $api_secret === $auth['api_secret'] && 'prevent' !== $api_key && 'prevent' !== $api_secret; + + /* + * 'prevent' is the sentinel stored when API credentials have not been + * generated yet. Reject it explicitly so a request that supplies + * 'prevent' (or empty/unset credentials) can never authenticate. + */ + if ('prevent' === $auth['api_key'] || 'prevent' === $auth['api_secret'] || '' === (string) $api_key || '' === (string) $api_secret) { + return false; + } + + /* + * Compare with hash_equals() so the check runs in constant time and does + * not leak the stored key/secret through response-timing differences. + */ + return hash_equals((string) $auth['api_key'], (string) $api_key) && hash_equals((string) $auth['api_secret'], (string) $api_secret); } /**