fix(captcha): 防止空 captcha_id 请求极验 403

custom 类型 schema 现按 array 深度净化保存;前台启用但凭据未齐时跳过渲染,Geetest 客户端也校验 captchaId。
This commit is contained in:
kate522 committed 2026-07-12 14:49:24 +08:00
1 parent 4fe4d79da0
commit b7dc3d170d
2 files changed
+97 -15

No files matched your search

@@ -365,7 +365,24 @@ class AKSRT_Admin_Schema_Provider {
case 'float': case 'float':
return (float) $value; return (float) $value;
case 'array': 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: default:
return sanitize_text_field((string) $value); return sanitize_text_field((string) $value);
} }
@@ -383,6 +400,8 @@ class AKSRT_Admin_Schema_Provider {
'radio' => 'string', 'radio' => 'string',
'checkbox' => 'array', 'checkbox' => 'array',
'repeater' => 'array', 'repeater' => 'array',
// 自定义面板(验证码服务商等)按对象存储
'custom' => 'array',
'color' => 'string', 'color' => 'string',
'icon' => 'string', 'icon' => 'string',
'media' => 'url', 'media' => 'url',
+77 -14
View File
@@ -21,6 +21,35 @@ function aksrt_get_captcha_config() {
} }
endif; 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')): if (!function_exists('aksrt_is_captcha_enabled_for')):
/** /**
* 检查指定场景是否启用人机验证 * 检查指定场景是否启用人机验证
@@ -32,8 +61,14 @@ function aksrt_is_captcha_enabled_for($scene) {
$cfg = aksrt_get_captcha_config(); $cfg = aksrt_get_captcha_config();
if (empty($cfg['enabled'])) return false; if (empty($cfg['enabled'])) return false;
if (empty($cfg['active_provider'])) 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(); $disabled = isset($cfg['disabled_scenarios']) ? $cfg['disabled_scenarios'] : array();
return !in_array($scene, $disabled); return !in_array($scene, $disabled, true);
} }
endif; endif;
@@ -516,6 +551,18 @@ function aksrt_render_captcha($scene = 'comment') {
if (!aksrt_is_captcha_enabled_for($scene)) return; if (!aksrt_is_captcha_enabled_for($scene)) return;
$provider = $cfg['active_provider']; $provider = $cfg['active_provider'];
$pcfg = isset($cfg[$provider]) && is_array($cfg[$provider]) ? $cfg[$provider] : array(); $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 '<div class="aksrt-captcha-misconfig" style="font-size:12px;color:#d03050;margin:8px 0;">';
echo esc_html(sprintf(
/* translators: %s: captcha provider key */
__('人机验证服务商「%s」已启用但凭据未配齐,前台已跳过渲染。请到「功能&权限 → 登录权限」补全配置。', 'aksrt'),
$provider
));
echo '</div>';
}
return;
}
$uid = 'aksrt-captcha-' . $scene . '-' . wp_unique_id(); $uid = 'aksrt-captcha-' . $scene . '-' . wp_unique_id();
$vid = 'captcha_verified_' . $scene . '_' . wp_unique_id(); $vid = 'captcha_verified_' . $scene . '_' . wp_unique_id();
@@ -526,20 +573,20 @@ function aksrt_render_captcha($scene = 'comment') {
'scene' => $scene, 'scene' => $scene,
'uid' => $uid, 'uid' => $uid,
'vid' => $vid, '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( 'cf' => array(
'site_key' => isset($pcfg['site_key']) ? $pcfg['site_key'] : '', 'site_key' => isset($pcfg['site_key']) ? (string) $pcfg['site_key'] : '',
'theme' => isset($pcfg['theme']) ? $pcfg['theme'] : 'auto', 'theme' => isset($pcfg['theme']) ? (string) $pcfg['theme'] : 'auto',
'size' => isset($pcfg['size']) ? $pcfg['size'] : 'normal' 'size' => isset($pcfg['size']) ? (string) $pcfg['size'] : 'normal'
), ),
'gt' => array( 'gt' => array(
'captcha_id' => isset($pcfg['captcha_id']) ? $pcfg['captcha_id'] : '', 'captcha_id' => isset($pcfg['captcha_id']) ? trim((string) $pcfg['captcha_id']) : '',
'product' => isset($pcfg['product']) ? $pcfg['product'] : 'popup' 'product' => isset($pcfg['product']) && $pcfg['product'] !== '' ? (string) $pcfg['product'] : 'bind',
), ),
'al' => array( 'al' => array(
'region' => isset($pcfg['region']) ? $pcfg['region'] : 'cn', 'region' => isset($pcfg['region']) ? (string) $pcfg['region'] : 'cn',
'prefix' => isset($pcfg['prefix']) ? $pcfg['prefix'] : '', 'prefix' => isset($pcfg['prefix']) ? (string) $pcfg['prefix'] : '',
'scene_id' => isset($pcfg['scene_id']) ? $pcfg['scene_id'] : '' 'scene_id' => isset($pcfg['scene_id']) ? (string) $pcfg['scene_id'] : ''
), ),
); );
?> ?>
@@ -921,10 +968,17 @@ function init(){
// ============ 极验 GeeTest v4 ============ // ============ 极验 GeeTest v4 ============
function loadGeetest(){ function loadGeetest(){
showLoading(); 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(){ function render(){
clearLoading(); 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; window['__gt_'+C.uid]=capIns;
function onSuccess(){ function onSuccess(){
var r=capIns.getValidate(); var r=capIns.getValidate();
@@ -1162,10 +1216,19 @@ function init(){
loadGeetest=function(){ loadGeetest=function(){
showLoading(); 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(){ function render(){
clearLoading(); 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; window['__gt_'+C.uid]=capIns;
function onSuccess(){ function onSuccess(){
var result=capIns.getValidate(); var result=capIns.getValidate();