From 7660c3a43d6510ca89de2fd500eb7fc2033c5e6a Mon Sep 17 00:00:00 2001 From: latenighthackathon Date: Mon, 20 Apr 2026 22:26:54 -0500 Subject: [PATCH] fix[faustwp]: (#2310) use hash_equals for secret key comparison MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace timing-vulnerable === comparisons with hash_equals() for the shared secret key in rest_authorize_permission_callback(), wpac_authorize_permission_callback(), and filter_introspection(). The codebase already uses hash_equals() for HMAC validation in auth/functions.php — these three spots were missed. Also sanitize the $_SERVER['HTTP_X_FAUST_SECRET'] superglobal with wp_unslash() and sanitize_text_field() per WordPress coding standards before passing to hash_equals(). Split out from #2312 per @josephfusco review feedback — the blockset cleanup half moves to a separate PR so each half can be reviewed and reverted independently. --- .changeset/hash-equals-secret-comparison.md | 5 +++++ plugins/faustwp/includes/graphql/callbacks.php | 5 +++-- plugins/faustwp/includes/rest/callbacks.php | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .changeset/hash-equals-secret-comparison.md diff --git a/.changeset/hash-equals-secret-comparison.md b/.changeset/hash-equals-secret-comparison.md new file mode 100644 index 000000000..36a3702a8 --- /dev/null +++ b/.changeset/hash-equals-secret-comparison.md @@ -0,0 +1,5 @@ +--- +"@faustwp/wordpress-plugin": patch +--- + +fix[faustwp]: use hash_equals() for constant-time secret key comparison in REST and GraphQL permission callbacks diff --git a/plugins/faustwp/includes/graphql/callbacks.php b/plugins/faustwp/includes/graphql/callbacks.php index 7c0cf1fdc..be3a4956e 100644 --- a/plugins/faustwp/includes/graphql/callbacks.php +++ b/plugins/faustwp/includes/graphql/callbacks.php @@ -66,8 +66,9 @@ function filter_introspection( $value, $default_value, $option_name, $section_fi return $value; } - $secret_key = get_secret_key(); - if ( $secret_key !== $_SERVER['HTTP_X_FAUST_SECRET'] ) { + $secret_key = get_secret_key(); + $faust_secret = sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FAUST_SECRET'] ) ); + if ( ! hash_equals( $secret_key, $faust_secret ) ) { return $value; } diff --git a/plugins/faustwp/includes/rest/callbacks.php b/plugins/faustwp/includes/rest/callbacks.php index 7574c025a..fa600ea97 100644 --- a/plugins/faustwp/includes/rest/callbacks.php +++ b/plugins/faustwp/includes/rest/callbacks.php @@ -419,7 +419,7 @@ function rest_authorize_permission_callback( \WP_REST_Request $request ) { $header_key = $request->get_header( 'x-faustwp-secret' ); if ( $secret_key && $header_key ) { - return $secret_key === $header_key; + return hash_equals( $secret_key, $header_key ); } return false; @@ -444,7 +444,7 @@ function wpac_authorize_permission_callback( \WP_REST_Request $request ) { $header_key = $request->get_header( 'x-wpe-headless-secret' ); if ( $secret_key && $header_key ) { - return $secret_key === $header_key; + return hash_equals( $secret_key, $header_key ); } return false;