From b7dc3d170df76dd8509d8cd5ac2873bc3a2722a5 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 12 Jul 2026 14:49:24 +0800 Subject: [PATCH] =?UTF-8?q?fix(captcha):=20=E9=98=B2=E6=AD=A2=E7=A9=BA=20c?= =?UTF-8?q?aptcha=5Fid=20=E8=AF=B7=E6=B1=82=E6=9E=81=E9=AA=8C=20403?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit custom 类型 schema 现按 array 深度净化保存;前台启用但凭据未齐时跳过渲染,Geetest 客户端也校验 captchaId。 --- .../class-aksrt-admin-schema-provider.php | 21 ++++- inc/classes/class-aksrt-captcha.php | 91 ++++++++++++++++--- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/inc/classes/admin/class-aksrt-admin-schema-provider.php b/inc/classes/admin/class-aksrt-admin-schema-provider.php index bbda23dd..555cb6e1 100644 --- a/inc/classes/admin/class-aksrt-admin-schema-provider.php +++ b/inc/classes/admin/class-aksrt-admin-schema-provider.php @@ -365,7 +365,24 @@ class AKSRT_Admin_Schema_Provider { case 'float': return (float) $value; case 'array': - return is_array($value) ? $value : []; + // 深层字符串净化,保留嵌套服务商配置(如 geetest.captcha_id) + if (!is_array($value)) { + return []; + } + $out = []; + foreach ($value as $k => $v) { + $key = is_string($k) ? sanitize_key($k) : $k; + if (is_array($v)) { + $out[$key] = $this->sanitize_value($v, 'array'); + } elseif (is_bool($v)) { + $out[$key] = $v; + } elseif (is_int($v) || is_float($v)) { + $out[$key] = $v; + } else { + $out[$key] = sanitize_text_field((string) $v); + } + } + return $out; default: return sanitize_text_field((string) $value); } @@ -383,6 +400,8 @@ class AKSRT_Admin_Schema_Provider { 'radio' => 'string', 'checkbox' => 'array', 'repeater' => 'array', + // 自定义面板(验证码服务商等)按对象存储 + 'custom' => 'array', 'color' => 'string', 'icon' => 'string', 'media' => 'url', diff --git a/inc/classes/class-aksrt-captcha.php b/inc/classes/class-aksrt-captcha.php index 08500b42..83735127 100644 --- a/inc/classes/class-aksrt-captcha.php +++ b/inc/classes/class-aksrt-captcha.php @@ -21,6 +21,35 @@ function aksrt_get_captcha_config() { } endif; +if (!function_exists('aksrt_captcha_provider_is_configured')): +/** + * 当前服务商必填凭据是否齐全(避免空 captcha_id 打到极验 403)。 + * + * @param string $provider + * @param array $pcfg + * @return bool + */ +function aksrt_captcha_provider_is_configured($provider, $pcfg = array()) { + $provider = sanitize_key((string) $provider); + if ($provider === '') { + return false; + } + if (!is_array($pcfg)) { + $pcfg = array(); + } + $required = aksrt_captcha_required_fields($provider); + if (empty($required)) { + return false; + } + foreach ($required as $field) { + if (!isset($pcfg[$field]) || trim((string) $pcfg[$field]) === '') { + return false; + } + } + return true; +} +endif; + if (!function_exists('aksrt_is_captcha_enabled_for')): /** * 检查指定场景是否启用人机验证 @@ -32,8 +61,14 @@ function aksrt_is_captcha_enabled_for($scene) { $cfg = aksrt_get_captcha_config(); if (empty($cfg['enabled'])) return false; if (empty($cfg['active_provider'])) return false; + $provider = (string) $cfg['active_provider']; + $pcfg = isset($cfg[$provider]) && is_array($cfg[$provider]) ? $cfg[$provider] : array(); + // 启用但凭据未配齐时,前台不渲染/不拦截,避免空 captcha_id 请求第三方 403 + if (!aksrt_captcha_provider_is_configured($provider, $pcfg)) { + return false; + } $disabled = isset($cfg['disabled_scenarios']) ? $cfg['disabled_scenarios'] : array(); - return !in_array($scene, $disabled); + return !in_array($scene, $disabled, true); } endif; @@ -516,6 +551,18 @@ function aksrt_render_captcha($scene = 'comment') { if (!aksrt_is_captcha_enabled_for($scene)) return; $provider = $cfg['active_provider']; $pcfg = isset($cfg[$provider]) && is_array($cfg[$provider]) ? $cfg[$provider] : array(); + if (!aksrt_captcha_provider_is_configured($provider, $pcfg)) { + if (current_user_can('manage_options')) { + echo '
'; + echo esc_html(sprintf( + /* translators: %s: captcha provider key */ + __('人机验证服务商「%s」已启用但凭据未配齐,前台已跳过渲染。请到「功能&权限 → 登录权限」补全配置。', 'aksrt'), + $provider + )); + echo '
'; + } + return; + } $uid = 'aksrt-captcha-' . $scene . '-' . wp_unique_id(); $vid = 'captcha_verified_' . $scene . '_' . wp_unique_id(); @@ -526,20 +573,20 @@ function aksrt_render_captcha($scene = 'comment') { 'scene' => $scene, 'uid' => $uid, 'vid' => $vid, - 'tc' => array('app_id' => isset($pcfg['app_id']) ? $pcfg['app_id'] : ''), + 'tc' => array('app_id' => isset($pcfg['app_id']) ? (string) $pcfg['app_id'] : ''), 'cf' => array( - 'site_key' => isset($pcfg['site_key']) ? $pcfg['site_key'] : '', - 'theme' => isset($pcfg['theme']) ? $pcfg['theme'] : 'auto', - 'size' => isset($pcfg['size']) ? $pcfg['size'] : 'normal' + 'site_key' => isset($pcfg['site_key']) ? (string) $pcfg['site_key'] : '', + 'theme' => isset($pcfg['theme']) ? (string) $pcfg['theme'] : 'auto', + 'size' => isset($pcfg['size']) ? (string) $pcfg['size'] : 'normal' ), 'gt' => array( - 'captcha_id' => isset($pcfg['captcha_id']) ? $pcfg['captcha_id'] : '', - 'product' => isset($pcfg['product']) ? $pcfg['product'] : 'popup' + 'captcha_id' => isset($pcfg['captcha_id']) ? trim((string) $pcfg['captcha_id']) : '', + 'product' => isset($pcfg['product']) && $pcfg['product'] !== '' ? (string) $pcfg['product'] : 'bind', ), 'al' => array( - 'region' => isset($pcfg['region']) ? $pcfg['region'] : 'cn', - 'prefix' => isset($pcfg['prefix']) ? $pcfg['prefix'] : '', - 'scene_id' => isset($pcfg['scene_id']) ? $pcfg['scene_id'] : '' + 'region' => isset($pcfg['region']) ? (string) $pcfg['region'] : 'cn', + 'prefix' => isset($pcfg['prefix']) ? (string) $pcfg['prefix'] : '', + 'scene_id' => isset($pcfg['scene_id']) ? (string) $pcfg['scene_id'] : '' ), ); ?> @@ -921,10 +968,17 @@ function init(){ // ============ 极验 GeeTest v4 ============ function loadGeetest(){ showLoading(); - var product=C.gt.product||'bind'; + var captchaId=(C.gt && C.gt.captcha_id) ? String(C.gt.captcha_id).trim() : ''; + var product=(C.gt && C.gt.product) ? String(C.gt.product) : 'bind'; + if(!captchaId){ + clearLoading(); + showTip('极验 Captcha ID 未配置,请到后台「功能&权限 → 登录权限」填写并保存','#e74c3c'); + triggerCaptcha=function(){ showTip('极验 Captcha ID 未配置','#e74c3c'); }; + return; + } function render(){ clearLoading(); - initGeetest4({captchaId:C.gt.captcha_id, product:product, hideSuccess:true}, function(capIns){ + initGeetest4({captchaId:captchaId, product:product, hideSuccess:true}, function(capIns){ window['__gt_'+C.uid]=capIns; function onSuccess(){ var r=capIns.getValidate(); @@ -1162,10 +1216,19 @@ function init(){ loadGeetest=function(){ showLoading(); - var product=C.gt.product||'bind'; + var captchaId=(C.gt && C.gt.captcha_id) ? String(C.gt.captcha_id).trim() : ''; + var product=(C.gt && C.gt.product) ? String(C.gt.product) : 'bind'; + if(!captchaId){ + clearLoading(); + showTip('极验 Captcha ID 未配置,请到后台「功能&权限 → 登录权限」填写并保存', '#e74c3c'); + triggerCaptcha=function(){ + showTip('极验 Captcha ID 未配置', '#e74c3c'); + }; + return; + } function render(){ clearLoading(); - initGeetest4({captchaId:C.gt.captcha_id, product:product, hideSuccess:true}, function(capIns){ + initGeetest4({captchaId:captchaId, product:product, hideSuccess:true}, function(capIns){ window['__gt_'+C.uid]=capIns; function onSuccess(){ var result=capIns.getValidate();