Skip to content

Commit df10f8e

Browse files
author
Hai Zheng
committed
v7.9.1-rc1
1 parent 15267e1 commit df10f8e

5 files changed

Lines changed: 60 additions & 34 deletions

File tree

litespeed-cache.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Plugin Name: LiteSpeed Cache
44
* Plugin URI: https://www.litespeedtech.com/products/cache-plugins/wordpress-acceleration
55
* Description: High-performance page caching and site optimization from LiteSpeed
6-
* Version: 7.9.1-b5
6+
* Version: 7.9.1-rc1
77
* Author: LiteSpeed Technologies
88
* Author URI: https://www.litespeedtech.com
99
* License: GPLv3
@@ -35,7 +35,7 @@
3535
return;
3636
}
3737

38-
! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '7.9.1-b5' );
38+
! defined( 'LSCWP_V' ) && define( 'LSCWP_V', '7.9.1-rc1' );
3939

4040
! defined( 'LSCWP_CONTENT_DIR' ) && define( 'LSCWP_CONTENT_DIR', WP_CONTENT_DIR );
4141
! defined( 'LSCWP_DIR' ) && define( 'LSCWP_DIR', __DIR__ . '/' ); // Full absolute path '/var/www/html/***/wp-content/plugins/litespeed-cache/' or MU

src/cloud-auth-callback.trait.php

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
*/
1818
trait Cloud_Auth_Callback {
1919

20+
/**
21+
* Successfully authorized REST requests retained for WordPress's repeated permission check.
22+
*
23+
* @var array<string,array<string,mixed>>
24+
*/
25+
private $_validated_callback_requests = [];
26+
2027
/**
2128
* Encrypt data for cloud req
2229
*
@@ -95,8 +102,9 @@ private function _local_sign_sk() {
95102
* @return array<int,string> Binary public keys.
96103
*/
97104
private function _trusted_server_pks( $from_wpapi = false, $key_id = '' ) {
98-
$source = $from_wpapi ? 'wpapi' : 'qc';
99-
$ring = isset( self::SERVER_SIGN_KEYS[ $source ] ) ? self::SERVER_SIGN_KEYS[ $source ] : [];
105+
$environment = false !== strpos( $this->_cloud_server, 'preview.' ) ? 'preview' : 'prod';
106+
$source = $from_wpapi ? 'wpapi' : 'qc';
107+
$ring = isset( self::SERVER_SIGN_KEYS[ $environment ][ $source ] ) ? self::SERVER_SIGN_KEYS[ $environment ][ $source ] : [];
100108
if ( '' !== $key_id ) {
101109
$ring = isset( $ring[ $key_id ] ) ? [ $ring[ $key_id ] ] : [];
102110
}
@@ -328,8 +336,7 @@ private function _verify_signed( $signed_bytes, $signature_b64, $qc_ts, $qc_nonc
328336
*/
329337
public function validate_signed_callback( $request, $action ) {
330338
if (
331-
! is_object( $request ) || ! method_exists( $request, 'get_body' ) || ! method_exists( $request, 'get_header' ) ||
332-
empty( $this->_summary['pk_b64'] ) || ! is_string( $this->_summary['pk_b64'] ) || ! is_string( $action )
339+
! is_object( $request ) || ! method_exists( $request, 'get_body' ) || ! method_exists( $request, 'get_header' ) || ! is_string( $action )
333340
) {
334341
return $this->_callback_error( self::CALLBACK_ERR_REQUEST, 'Invalid signed callback request.' );
335342
}
@@ -368,7 +375,7 @@ public function validate_signed_callback( $request, $action ) {
368375
! is_array( $payload ) ||
369376
! isset( $payload['qc_sig_v'] ) || ! is_int( $payload['qc_sig_v'] ) || self::SIGN_VERSION !== $payload['qc_sig_v'] ||
370377
empty( $payload['qc_action'] ) || ! is_string( $payload['qc_action'] ) ||
371-
empty( $payload['wp_pk_b64'] ) || ! is_string( $payload['wp_pk_b64'] ) ||
378+
! isset( $payload['wp_pk_sha256'] ) || ! is_string( $payload['wp_pk_sha256'] ) || ! preg_match( '/^[a-f0-9]{64}$/D', $payload['wp_pk_sha256'] ) ||
372379
! isset( $payload['qc_ts'] ) || ! is_string( $payload['qc_ts'] ) ||
373380
! isset( $payload['qc_nonce'] ) || ! is_string( $payload['qc_nonce'] ) ||
374381
( isset( $payload['qc_key_id'] ) && ! is_string( $payload['qc_key_id'] ) )
@@ -379,19 +386,40 @@ public function validate_signed_callback( $request, $action ) {
379386
if ( ! hash_equals( $action, $payload['qc_action'] ) ) {
380387
return $this->_callback_error( self::CALLBACK_ERR_ACTION, 'Callback action does not match its route.' );
381388
}
382-
if ( ! hash_equals( (string) $this->_summary['pk_b64'], $payload['wp_pk_b64'] ) ) {
383-
return $this->_callback_error( self::CALLBACK_ERR_SITE, 'Callback site key does not match this site.' );
389+
390+
$request_key = spl_object_hash( $request ) . ':' . hash( 'sha256', $action );
391+
$fingerprint = hash( 'sha256', $body ) . hash( 'sha256', $content_type ) . hash( 'sha256', $content_encoding ) . hash( 'sha256', $signature_b64 );
392+
if (
393+
isset( $this->_validated_callback_requests[ $request_key ] ) &&
394+
$this->_validated_callback_requests[ $request_key ]['request'] === $request &&
395+
hash_equals( $this->_validated_callback_requests[ $request_key ]['fingerprint'], $fingerprint )
396+
) {
397+
return true;
384398
}
385399

386-
$key_id = isset( $payload['qc_key_id'] ) ? $payload['qc_key_id'] : '';
387-
return $this->_verify_signed(
400+
if ( empty( $this->_summary['pk_b64'] ) || ! is_string( $this->_summary['pk_b64'] ) ) {
401+
return $this->_callback_error( self::CALLBACK_ERR_REQUEST, 'Invalid signed callback request.' );
402+
}
403+
if ( ! hash_equals( hash( 'sha256', (string) $this->_summary['pk_b64'] ), $payload['wp_pk_sha256'] ) ) {
404+
return $this->_callback_error( self::CALLBACK_ERR_SITE, 'Callback site-key digest does not match this site.' );
405+
}
406+
407+
$key_id = isset( $payload['qc_key_id'] ) ? $payload['qc_key_id'] : '';
408+
$verified = $this->_verify_signed(
388409
$body,
389410
trim( $signature_b64 ),
390411
$payload['qc_ts'],
391412
$payload['qc_nonce'],
392413
self::SIGN_ACTION_NOTIFY_IMG === $action,
393414
$key_id
394415
);
416+
if ( true === $verified ) {
417+
$this->_validated_callback_requests[ $request_key ] = [
418+
'request' => $request,
419+
'fingerprint' => $fingerprint,
420+
];
421+
}
422+
return $verified;
395423
}
396424

397425
/**

src/cloud-auth-ip.trait.php

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,21 @@
1818
trait Cloud_Auth_IP {
1919

2020
/**
21-
* Request callback validation from Cloud
21+
* Confirm the signed Cloud callback path.
2222
*
2323
* @since 3.0
2424
* @access public
25-
* @param string|null $raw_body Verified request body.
2625
* @return array
2726
*/
28-
public function ip_validate( $raw_body = null ) {
29-
$payload = is_string( $raw_body ) ? json_decode( $raw_body, true, 32 ) : false;
30-
$hash = is_array( $payload ) && isset( $payload['hash'] ) && is_string( $payload['hash'] ) ? $payload['hash'] : '';
27+
public function ip_validate() {
3128
$site_pk = isset( $this->_summary['pk_b64'] ) && is_string( $this->_summary['pk_b64'] ) ? $this->_summary['pk_b64'] : '';
32-
if ( '' === $site_pk || ! preg_match( '/^[a-f0-9]{32}$/D', $hash ) ) {
29+
if ( '' === $site_pk ) {
3330
return self::err( 'lack_of_params' );
3431
}
3532

36-
if ( ! hash_equals( md5( substr( $site_pk, 0, 4 ) ), $hash ) ) {
37-
self::debug( '__callback IP request decryption failed' );
38-
return self::err( 'err_hash' );
39-
}
40-
41-
Control::set_nocache( 'Cloud IP hash validation' );
42-
43-
$resp_hash = md5( substr( $site_pk, 2, 4 ) );
44-
45-
self::debug( '__callback IP request hash: ' . $resp_hash );
33+
Control::set_nocache( 'Cloud IP validation' );
4634

47-
return self::ok( [ 'hash' => $resp_hash ] );
35+
return self::ok( [ 'hash' => md5( substr( $site_pk, 2, 4 ) ) ] );
4836
}
4937

5038
/**

src/cloud.cls.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,24 @@ class Cloud extends Base {
110110
const ITEM_SIGN_NONCE = 'sign_nonce.';
111111

112112
/**
113-
* Trusted callback keys. Add the next key before rotation and remove revoked keys in an update.
113+
* Trusted callback keys, isolated by environment. Add the next key before rotation and remove revoked keys in an update.
114114
*/
115115
const SERVER_SIGN_KEYS = [
116-
'qc' => [
117-
'qc-2025-01' => '1a8mxBAOPQ4SsyCncktY2O/CcN0hfv891qCYth7ay2I=',
116+
'prod' => [
117+
'qc' => [
118+
'qc-2025-01' => '1a8mxBAOPQ4SsyCncktY2O/CcN0hfv891qCYth7ay2I=',
119+
],
120+
'wpapi' => [
121+
'wpapi-2025-01' => 'g9hiXo+P/fX4FbOnZu5dDKpEQXpwuDSkuCkCaX3ZSpk=',
122+
],
118123
],
119-
'wpapi' => [
120-
'wpapi-2025-01' => 'g9hiXo+P/fX4FbOnZu5dDKpEQXpwuDSkuCkCaX3ZSpk=',
124+
'preview' => [
125+
'qc' => [
126+
'qc-2025-01' => 'm13AJlqQ5ir1ro1WMSK8h7Hjz0OAl4t6kepSAjqsMTY=',
127+
],
128+
'wpapi' => [
129+
'wpapi-2025-01' => 'g9hiXo+P/fX4FbOnZu5dDKpEQXpwuDSkuCkCaX3ZSpk=',
130+
],
121131
],
122132
];
123133

src/rest.cls.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ public function guest_sync() {
174174
* @return mixed
175175
*/
176176
public function ip_validate( $request ) {
177-
return $this->cls( 'Cloud' )->ip_validate( $request->get_body() );
177+
return $this->cls( 'Cloud' )->ip_validate();
178178
}
179179

180180
/**

0 commit comments

Comments
 (0)