get_header('x-agent-signature'); $timestamp = $request->get_header('x-agent-timestamp'); $nonce = $request->get_header('x-agent-nonce'); if ($signature && $timestamp && $nonce) { // 验证 RSA 签名,确认是合法后端调用 return self::validate_backend_request($request); } // 前端调用 → 验证 WooCommerce Session return self::validate_frontend_request($request); } /** * 验证前端请求:WooCommerce Session * Session Cookie 是 HttpOnly 的,JavaScript 无法读取,确保安全性 */ private static function validate_frontend_request(WP_REST_Request $request) { // WooCommerce 检查 if (!class_exists('WooCommerce')) { return new WP_Error('wc_missing', 'WooCommerce is not active', array('status' => 503)); } // 获取并验证 Session Cookie $wc_hash = defined('COOKIEHASH') ? COOKIEHASH : md5(get_option('siteurl')); $cookie_name = 'wp_woocommerce_session_' . $wc_hash; // 初始化 Session $cookie_value = sanitize_text_field(wp_unslash($_COOKIE[$cookie_name])); $parts = explode('||', $cookie_value); if (count($parts) >= 2) { $session_id = $parts[0]; WC()->session->init($session_id); } // 验证 Session 是否有效 if (!WC()->session->has_session()) { return new WP_Error( 'session_invalid', 'Invalid WooCommerce session', array('status' => 401) ); } return true; } /** * 验证后端请求:RSA 签名 */ private static function validate_backend_request(WP_REST_Request $request) { // 提取必要头 $signature = sanitize_text_field((string) $request->get_header('x-agent-signature')); $timestamp = sanitize_text_field((string) $request->get_header('x-agent-timestamp')); $nonce = sanitize_text_field((string) $request->get_header('x-agent-nonce')); // 必须三者都有 if (!$signature || !$timestamp || !$nonce) { return new WP_Error( 'auth_missing', 'Missing X-Agent-Signature / X-Agent-Timestamp / X-Agent-Nonce', array('status' => 401) ); } // 验证 nonce 格式 if (strlen($nonce) > 128 || !preg_match('/^[A-Za-z0-9._-]+$/', $nonce)) { return new WP_Error( 'auth_missing', 'Invalid nonce', array('status' => 401) ); } // 验证 timestamp 格式 if (!ctype_digit($timestamp)) { return new WP_Error( 'auth_missing', 'Invalid timestamp', array('status' => 401) ); } // 验证 timestamp 时效性(±5 分钟) $current_time = time(); $req_time = (int) $timestamp; if (abs($current_time - $req_time) > 300) { return new WP_Error( 'auth_missing', 'expired request', array('status' => 401) ); } // 验证 nonce 唯一性(防重放) if (!self::is_nonce_valid($nonce, $req_time)) { return new WP_Error( 'auth_missing', 'expired nonce', array('status' => 401) ); } // 获取原始请求体 $payload = $request->get_body(); if (!is_string($payload)) { $payload = ''; } // 获取 Shomi 公钥 $public_key_pem = self::get_antomshomi_public_key(); if (!$public_key_pem) { return new WP_Error( 'auth_missing', 'no Shomi public Key', array('status' => 401) ); } // 构造待签名字符串 $data_to_sign = $payload . "\n" . $timestamp . "\n" . $nonce; // 验证 RSA-SHA256 签名 if (!self::verify_rsa_sha256_signature($data_to_sign, $signature, $public_key_pem)) { return new WP_Error( 'auth_missing', 'invalid signature', array('status' => 401) ); } return true; } /** * 验证 nonce 是否有效(未使用过) */ private static function is_nonce_valid($nonce, $timestamp) { $nonce_key = 'antom_shomi_nonce_' . md5($nonce); // 如果已存在 → 重复使用 if (get_transient($nonce_key) !== false) { return false; } // 记录 nonce,有效期 5 分钟 set_transient($nonce_key, 1, 300); return true; } /** * 验证 RSA-SHA256 签名 * 删除了 openssl_free_key() 因为 PHP 8.0+ 已废弃 */ private static function verify_rsa_sha256_signature($data, $signature, $public_key_pem) { $signature_bin = base64_decode($signature, true); if ($signature_bin === false) { return false; } $public_key = openssl_pkey_get_public($public_key_pem); if (!$public_key) { return false; } $result = openssl_verify($data, $signature_bin, $public_key, OPENSSL_ALGO_SHA256); // openssl_free_key() is deprecated in PHP 8.0+, keys are auto-freed return $result === 1; } /** * 获取 Shomi 公钥 */ private static function get_antomshomi_public_key() { return "-----BEGIN PUBLIC KEY-----\n" . "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqO55DYBRFusbb1iMruYA\n" . "WlVKUrUUFPmSxKgzhX365j8io3rOJwQIePrfb74IwLWJOnfVn4uiLgP8PoQBShxx\n" . "AYCWdzsV7uSs7P5efFoGeW3X5YRtOgfwa3jWbxw85vmXsunT254MWAHX8Hl9JVnf\n" . "4XyWjIOIf0Tn+6svvi7FbAXbz9+GKvVu8ZGTiPJBz8ZMRykkMragsjGb31csTQrT\n" . "Js+Uukojzek9r8dTKk87JXsuItQ6dffHi7Rr+fw8mkrXFczN+lXUso4bVvSiTnWv\n" . "bnrLlqamHFGJ454nEr5PzNvozko5LzfObLczTd2nd9XjVS6jBkxM2CTLhGIgqNkT\n" . "YwIDAQAB\n" . "-----END PUBLIC KEY-----"; } }