| @@ -28,8 +28,47 @@ | ||
| 28 | 28 | * Register hooks. |
| 29 | 29 | */ |
| 30 | 30 | public function boot(): void { |
| 31 | 31 | add_action( 'rest_api_init', array( $this, 'register' ) ); |
| 32 | + // After core's cookie check (priority 100). | |
| 33 | + add_filter( 'rest_authentication_errors', array( $this, 'accept_cached_page_token' ), 101 ); | |
| 34 | + } | |
| 35 | + | |
| 36 | + /** | |
| 37 | + * (2.0.4) Let a page-cached beacon through on its cache-safe token. | |
| 38 | + * | |
| 39 | + * A cached page carries the nonce it was rendered with, which expires | |
| 40 | + * within a day. WordPress rejects ANY REST request carrying an invalid | |
| 41 | + * nonce (403 rest_cookie_invalid_nonce) before the route runs — logged in | |
| 42 | + * or not — so the token those pages also send was never checked, and every | |
| 43 | + * beacon from a day-old cached page failed. Since 2.0.4 the scripts send | |
| 44 | + * the token alone; this covers pages cached by older versions. Only for | |
| 45 | + * the two public endpoints, only with a valid token, and the request then | |
| 46 | + * runs as no user — exactly as if no nonce had been sent. | |
| 47 | + * | |
| 48 | + * @param mixed $result Authentication result so far. | |
| 49 | + * @return mixed | |
| 50 | + */ | |
| 51 | + public function accept_cached_page_token( $result ) { | |
| 52 | + if ( ! is_wp_error( $result ) || 'rest_cookie_invalid_nonce' !== $result->get_error_code() ) { | |
| 53 | + return $result; | |
| 54 | + } | |
| 55 | + | |
| 56 | + $route = isset( $GLOBALS['wp']->query_vars['rest_route'] ) ? untrailingslashit( (string) $GLOBALS['wp']->query_vars['rest_route'] ) : ''; | |
| 57 | + | |
| 58 | + if ( ! in_array( $route, array( '/' . self::NS . '/beacon', '/' . self::NS . '/async-fonts' ), true ) ) { | |
| 59 | + return $result; | |
| 60 | + } | |
| 61 | + | |
| 62 | + $token = isset( $_GET['_eftoken'] ) ? (string) wp_unslash( $_GET['_eftoken'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 63 | + | |
| 64 | + if ( '' === $token || ! hash_equals( Settings::beacon_token(), $token ) ) { | |
| 65 | + return $result; | |
| 66 | + } | |
| 67 | + | |
| 68 | + wp_set_current_user( 0 ); | |
| 69 | + | |
| 70 | + return true; | |
| 32 | 71 | } |
| 33 | 72 | |
| 34 | 73 | /** |
| 35 | 74 | * Register routes. |