diff options
| author | Murilo Ijanc <[email protected]> | 2026-07-26 16:41:48 -0300 |
|---|---|---|
| committer | Murilo Ijanc <[email protected]> | 2026-07-26 16:47:14 -0300 |
| commit | 535dbda6b48171f8115b455e54790de94d97d42a (patch) | |
| tree | 5c0410011e61a883bdf063ee612875a637fb1d22 /includes/class-cepis-woo-token.php | |
| download | woocommerce-0.1.0.tar.gz | |
Initial importv0.1.0
Diffstat (limited to 'includes/class-cepis-woo-token.php')
| -rw-r--r-- | includes/class-cepis-woo-token.php | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/includes/class-cepis-woo-token.php b/includes/class-cepis-woo-token.php new file mode 100644 index 0000000..1d1b9f4 --- /dev/null +++ b/includes/class-cepis-woo-token.php @@ -0,0 +1,76 @@ +<?php +/** + * The token that guards the server-side proxy route. + * + * @package CepisWooCommerce + */ + +declare(strict_types=1); + +defined( 'ABSPATH' ) || exit; + +/** + * A short-lived nonce for the proxy route, derived the way WordPress derives + * its own but without binding to a login session. + * + * The bundled widget fetches with `credentials: "omit"` — deliberately, since + * a CEP lookup has no business carrying the shopper's cookies. The proxy + * therefore never sees an authentication cookie and every request reaches it + * anonymous, whether or not the shopper is logged in. A cookie-bound + * {@see wp_create_nonce()} would embed a session token in the page that the + * request could not present, so it would verify for guests and fail for + * everyone with an account: the worst possible failure mode, invisible in + * testing and broken in production. + * + * What the token has to defend against is the only threat this endpoint + * actually has — a third party pointing their own site at this store's proxy + * and using it as a free CEP gateway. A value tied to the site's salts and to + * {@see wp_nonce_tick()} does exactly that: it cannot be produced without the + * salts, and it stops working within a day. + * + * Same lifetime as a WordPress nonce: the current tick and the one before it + * are both accepted, so a checkout page held open overnight keeps working. + */ +final class Cepis_Woo_Token { + + /** + * Namespaces the hash, so a token cannot be replayed against anything else + * on the site that hashes with the `nonce` salt. + */ + private const ACTION = 'cepis_woo_proxy_lookup'; + + /** + * Mint the token to embed in the checkout page. + */ + public static function create(): string { + return self::hash( wp_nonce_tick( self::ACTION ) ); + } + + /** + * Check a token presented by the browser. + * + * Compared with {@see hash_equals()} so that a wrong token takes the same + * time to reject as a right one. + * + * @param string $token Token as it arrived in the request. + */ + public static function verify( string $token ): bool { + if ( '' === $token ) { + return false; + } + + $tick = wp_nonce_tick( self::ACTION ); + + return hash_equals( self::hash( $tick ), $token ) + || hash_equals( self::hash( $tick - 1 ), $token ); + } + + /** + * Derive the token for one tick. + * + * @param float $tick Nonce tick to derive from. + */ + private static function hash( float $tick ): string { + return substr( wp_hash( $tick . '|' . self::ACTION, 'nonce' ), -12, 10 ); + } +} |