fix(captcha): close human-verification loop end-to-end

Rewrite captcha core around aksrt_captcha_config only, require captcha
on login/comment/SMS/phone auth and related gates, wire frontend token
collection, and remove dead dual-source security settings plus slider enqueue.
This commit is contained in:
2026-07-13 19:52:30 +08:00
parent a0808bbe54
commit 02f77b756c
21 changed files with 1163 additions and 1515 deletions
@@ -173,8 +173,12 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
$email_gate_enabled = false;
}
$captcha_comment_enabled = !empty($settings['captcha_comment_enabled']);
$captcha_download_enabled = !empty($settings['captcha_download_enabled']);
$captcha_summary = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'configured' => false, 'scenes' => []];
$captcha_comment_enabled = !empty($captcha_summary['scenes']['comment']);
$features = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_cms_features() : [];
$captcha_download_enabled = !empty($features['download_captcha']) && !empty($captcha_summary['scenes']['download']);
$risk_switches = [
'show_unapproved_comments' => !empty($settings['show_unapproved_comments']),
@@ -191,7 +195,8 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
'content_audit_configured' => !empty($services['audit']['configured']),
'ip_geo_enabled' => !empty($services['ip_geo']['enabled']),
'ip_geo_configured' => !empty($services['ip_geo']['configured']),
'captcha_enabled' => $captcha_comment_enabled || $captcha_download_enabled,
'captcha_enabled' => !empty($captcha_summary['enabled']) && !empty($captcha_summary['configured']),
'captcha_summary' => $captcha_summary,
'risk_switches' => $risk_switches,
'services' => $services,
],
@@ -452,17 +457,19 @@ class AKSRT_Admin_Extension_Controller extends AKSRT_Admin_Controller_Base {
$services['wechat'] = ['enabled' => false, 'provider' => '', 'configured' => false, 'available' => false, 'verified' => false, 'transport' => '', 'missing_fields' => []];
}
// Captcha
$settings = AKSRT_Settings::get_all();
$captcha_en = class_exists('AKSRT_Captcha') && !empty($settings['captcha_enabled']);
// Captcha — 唯一真相源 aksrt_captcha_config
$captcha = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
$services['captcha'] = [
'enabled' => $captcha_en,
'provider' => sanitize_key((string) ($settings['captcha_provider'] ?? '')),
'configured' => $captcha_en,
'available' => $captcha_en,
'enabled' => !empty($captcha['enabled']) && !empty($captcha['configured']),
'provider' => (string) ($captcha['provider'] ?? ''),
'configured' => !empty($captcha['configured']),
'available' => !empty($captcha['configured']),
'verified' => false,
'transport' => 'http_api',
'missing_fields' => [],
'scenes' => $captcha['scenes'] ?? [],
];
// IP Geo
@@ -181,6 +181,17 @@ trait AKSRT_Admin_User_Controller_Auth_Phone_Operations {
public function auth_send_sms_code(WP_REST_Request $request): WP_REST_Response {
$params = $this->get_json_params($request);
$phone = sanitize_text_field((string) ($params['phone'] ?? ''));
$scene = sanitize_key((string) ($params['scene'] ?? 'register'));
if (!array_key_exists($scene, function_exists('aksrt_captcha_scenes') ? aksrt_captcha_scenes() : [])) {
$scene = 'register';
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha($scene, is_array($params) ? $params : []);
if ($captcha_result !== true) {
return $this->error((string) $captcha_result, 400);
}
}
if (!class_exists('AKSRT_SMS')) {
return $this->error('短信服务不可用');
@@ -52,28 +52,8 @@ class AKSRT_Admin_Schema_Extensions_Security {
['key' => 'time_format', 'label' => '时间显示格式', 'type' => 'input', 'default' => 'n月j日 H:i'],
['key' => 'remove_more_wp_head', 'label' => '移除WP head多余代码', 'type' => 'switch', 'default' => true],
]],
['key' => 'verification', 'title' => '验证设置', 'fields' => [
['key' => 'verification_comment_s', 'label' => '评论需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_links_s', 'label' => '链接需要验证', 'type' => 'switch', 'default' => true],
['key' => 'verification_newposts_s', 'label' => '新文章需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_bbspost_s', 'label' => '论坛帖子需要验证', 'type' => 'switch', 'default' => false],
['key' => 'verification_signin_exclude', 'label' => '登录人机验证排除账号', 'type' => 'input', 'default' => ''],
['key' => 'user_verification_type', 'label' => '验证类型', 'type' => 'radio', 'default' => 'slider', 'options' => [
['label' => '图形验证码', 'value' => 'image'],
['label' => '滑块验证', 'value' => 'slider'],
['label' => '腾讯验证码', 'value' => 'tcaptcha'],
['label' => '极验验证码', 'value' => 'geetest'],
['label' => '关闭验证', 'value' => 'null'],
]],
]],
['key' => 'captcha', 'title' => '验证码设置', 'fields' => [
['key' => 'geetest_enabled', 'label' => '极验验证码', 'type' => 'switch', 'default' => false],
['key' => 'geetest_id', 'label' => '极验 ID', 'type' => 'input', 'default' => ''],
['key' => 'geetest_key', 'label' => '极验 Key', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_enabled', 'label' => '腾讯云验证码', 'type' => 'switch', 'default' => false],
['key' => 'tcaptcha_appid', 'label' => '腾讯云 AppID', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_secret_id', 'label' => '腾讯云 SecretId', 'type' => 'input', 'default' => ''],
['key' => 'tcaptcha_secret_key', 'label' => '腾讯云 SecretKey', 'type' => 'input', 'default' => ''],
['key' => 'captcha-note', 'title' => '人机验证', 'fields' => [
['key' => 'captcha_redirect_hint', 'label' => '配置入口', 'type' => 'notice', 'alertType' => 'info', 'alertContent' => '人机验证(腾讯云 / 阿里云 / Cloudflare / 极验)请到「功能&权限 → 登录权限」统一配置;本页不再维护验证码字段。'],
]],
['key' => 'rate-limit', 'title' => '频率限制', 'fields' => [
['key' => 'brush_limit_post_enabled', 'label' => '文章发布限制', 'type' => 'switch', 'default' => false],
@@ -137,7 +137,7 @@ class AKSRT_Admin_Schema_Permissions {
['key' => 'download_role_size', 'label' => '角色文件大小限制', 'type' => 'textarea', 'default' => '', 'help' => '格式: role|max_size_mb,每行一条'],
]],
['key' => 'captcha', 'title' => '安全设置', 'fields' => [
['key' => 'download_captcha', 'label' => '下载需要验证码', 'type' => 'switch', 'default' => false],
['key' => 'download_captcha_hint', 'label' => '下载验证码', 'type' => 'notice', 'alertType' => 'info', 'alertContent' => '下载验证码门槛在「功能&权限 → 下载权限」页(cms_features.download_captcha)维护;服务商与场景在「登录权限」。'],
['key' => 'download_ip_limit', 'label' => 'IP下载限制', 'type' => 'number', 'default' => 0, 'min' => 0, 'help' => '同一IP每小时最大下载次数,0为不限制'],
]],
],
@@ -47,12 +47,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => $password_check];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('register')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('register', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -154,12 +152,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('bind')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('bind', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -371,12 +367,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('验证入口无效或已过期,请重新发起验证流程。', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('verify_email')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -417,12 +411,10 @@ trait AKSRT_Auth_Service_Part_1a {
return ['success' => false, 'error' => __('该账号已完成邮箱验证,无需重复发送', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('verify_email')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('verify_email', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -110,12 +110,10 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('安全验证失败,请刷新页面重试', 'aksrt')];
}
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('lostpassword')) {
if (function_exists('aksrt_verify_captcha')) {
$captcha_result = aksrt_verify_captcha($post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('lostpassword', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
@@ -247,6 +245,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('密码至少 6 位', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('register', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
// 验证 SMS 验证码
if (class_exists('AKSRT_SMS')) {
$verify_result = AKSRT_SMS::get_instance()->verify_code($phone, $code);
@@ -321,6 +326,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$user = self::get_user_by_phone($phone);
if (!$user) {
return ['success' => false, 'error' => __('该手机号未注册', 'aksrt')];
@@ -359,6 +371,13 @@ trait AKSRT_Auth_Service_Part_1b {
return ['success' => false, 'error' => __('手机号格式不正确', 'aksrt')];
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
// 验证 SMS 验证码
if (class_exists('AKSRT_SMS')) {
$verify_result = AKSRT_SMS::get_instance()->verify_code($phone, $code);
+7
View File
@@ -368,6 +368,13 @@ final class AKSRT_Auth_Service {
}
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('login', $post);
if ($captcha_result !== true) {
return ['success' => false, 'error' => $captcha_result];
}
}
$creds = [
'user_login' => $resolved_login,
'user_password' => (string) ($post['pwd'] ?? ''),
File diff suppressed because it is too large Load Diff
+11 -5
View File
@@ -227,6 +227,17 @@ class AKSRT_Comment_Actions {
], 403);
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha('comment', $params);
if ($captcha_result !== true) {
return new \WP_REST_Response([
'success' => false,
'code' => 'captcha_failed',
'message' => (string) $captcha_result,
], 400);
}
}
$user = wp_get_current_user();
$commentdata = [
'comment_post_ID' => $post_id,
@@ -263,11 +274,6 @@ class AKSRT_Comment_Actions {
$_POST[$k] = $params[$k];
}
}
foreach ($params as $k => $v) {
if (is_string($k) && (str_contains($k, 'captcha') || str_starts_with($k, 'g-recaptcha') || str_starts_with($k, 'cf-turnstile') || str_starts_with($k, 'h-captcha'))) {
$_POST[$k] = $v;
}
}
$comment_id = wp_new_comment(wp_slash($commentdata), true);
if (is_wp_error($comment_id)) {
+10 -9
View File
@@ -162,16 +162,17 @@ class AKSRT_Download {
if (is_wp_error($gate)) return $gate;
$paid_settings = AKSRT_Settings::get_paid_content_settings();
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
if ($need_captcha && function_exists('aksrt_is_captcha_enabled_for') && !aksrt_is_captcha_enabled_for('download')) {
$need_captcha = false;
}
if ($need_captcha && function_exists('aksrt_verify_captcha')) {
$need_captcha = !empty($paid_settings['download_captcha']) || !empty($meta['require_captcha']);
// 下载双开关:cms_features.download_captcha(或单资源强制)且场景未停用。
if ($need_captcha && function_exists('aksrt_require_captcha')) {
$cap_payload = isset($params['captcha']) && is_array($params['captcha']) ? $params['captcha'] : [];
$verify = aksrt_verify_captcha($cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
// 场景未启用时 require 直接放行;启用时强制校验 token。
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('download')) {
$verify = aksrt_require_captcha('download', $cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
}
}
+9 -9
View File
@@ -95,15 +95,15 @@ class AKSRT_Signin {
}
// 人机验证(若启用)
if (function_exists('aksrt_is_captcha_enabled_for') && aksrt_is_captcha_enabled_for('signin')) {
$body = $request->get_json_params();
$cap_payload = isset($body['captcha']) && is_array($body['captcha']) ? $body['captcha'] : [];
if (function_exists('aksrt_verify_captcha')) {
$verify = aksrt_verify_captcha($cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
if (function_exists('aksrt_require_captcha')) {
$body = $request->get_json_params();
$cap_payload = is_array($body) && isset($body['captcha']) && is_array($body['captcha'])
? $body['captcha']
: (is_array($body) ? $body : []);
$verify = aksrt_require_captcha('signin', $cap_payload);
if ($verify !== true) {
$msg = is_string($verify) ? $verify : __('人机验证失败', 'aksrt');
return new WP_Error('aksrt_captcha_failed', $msg, ['status' => 400]);
}
}
+9 -4
View File
@@ -296,10 +296,15 @@ class AKSRT_SMS {
wp_send_json_error(['message' => __('手机号格式不正确', 'aksrt')]);
}
// 人机验证钩子(对齐 zibll 的 man_machine_verification)
$captcha_result = apply_filters('aksrt_sms_captcha_verify', true, $phone);
if ($captcha_result !== true) {
wp_send_json_error(['message' => $captcha_result ?: __('人机验证失败', 'aksrt')]);
$scene = sanitize_key((string) ($_POST['scene'] ?? 'register'));
if (!array_key_exists($scene, function_exists('aksrt_captcha_scenes') ? aksrt_captcha_scenes() : [])) {
$scene = 'register';
}
if (function_exists('aksrt_require_captcha')) {
$captcha_result = aksrt_require_captcha($scene, wp_unslash($_POST));
if ($captcha_result !== true) {
wp_send_json_error(['message' => $captcha_result ?: __('人机验证失败', 'aksrt')]);
}
}
$result = $this->send_code($phone);
+6 -6
View File
@@ -1004,8 +1004,8 @@ add_action('wp_ajax_aksrt_send_email_code', function () {
wp_send_json_error( array( 'message' => __( '无效的验证场景', 'aksrt' ) ) );
}
if ( aksrt_is_captcha_enabled_for( $scene ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( $scene, wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -1113,8 +1113,8 @@ add_action('wp_ajax_aksrt_change_password', function () {
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_password' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_password', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -1178,8 +1178,8 @@ add_action('wp_ajax_aksrt_change_email', function () {
}
// 人机验证
if ( aksrt_is_captcha_enabled_for( 'change_email' ) ) {
$captcha_result = aksrt_verify_captcha( $_POST );
if ( function_exists( 'aksrt_require_captcha' ) ) {
$captcha_result = aksrt_require_captcha( 'change_email', wp_unslash( $_POST ) );
if ( $captcha_result !== true ) {
wp_send_json_error( array( 'message' => $captcha_result ) );
}
@@ -886,18 +886,6 @@ trait AKSRT_Scripts_Part_1b {
);
}
// Slider CAPTCHA (canvas-based puzzle verification)
$slider_captcha_js = get_template_directory() . '/assets/js/slider-captcha.js';
if (file_exists($slider_captcha_js)) {
wp_enqueue_script(
'aksrt-slider-captcha',
$base_uri . '/assets/js/slider-captcha.js',
[],
(string) filemtime($slider_captcha_js),
true
);
}
// Instant page prefetch (hover to preload)
$instant_page_js = get_template_directory() . '/assets/js/instant-page.js';
if (file_exists($instant_page_js)) {
@@ -182,63 +182,47 @@ class AKSRT_Settings_Capabilities {
}
/**
* 获取"网站安全"设置
* 获取"网站安全"设置(人机验证见 aksrt_get_captcha_status_summary)。
*/
public static function get_security_settings(): array {
$s = AKSRT_Settings::get_all();
$verification_type = sanitize_key((string) ($s['user_verification_type'] ?? 'slider'));
$allowed_types = ['image', 'slider', 'tcaptcha', 'geetest', 'null'];
if (!in_array($verification_type, $allowed_types, true)) {
$verification_type = 'slider';
}
$captcha = function_exists('aksrt_get_captcha_status_summary')
? aksrt_get_captcha_status_summary()
: ['enabled' => false, 'provider' => '', 'configured' => false, 'scenes' => []];
return [
'verification_type' => $verification_type,
'verification_comment_s' => !empty($s['verification_comment_s']),
'verification_newposts_s' => !empty($s['verification_newposts_s']),
'verification_bbspost_s' => !empty($s['verification_bbspost_s']),
'verification_links_s' => !empty($s['verification_links_s'] ?? true),
'verification_signin_exclude' => sanitize_text_field((string) ($s['verification_signin_exclude'] ?? '')),
'geetest_enabled' => !empty($s['geetest_enabled']),
'geetest_id' => sanitize_text_field((string) ($s['geetest_id'] ?? '')),
'geetest_key' => sanitize_text_field((string) ($s['geetest_key'] ?? '')),
'tcaptcha_enabled' => !empty($s['tcaptcha_enabled']),
'tcaptcha_appid' => sanitize_text_field((string) ($s['tcaptcha_appid'] ?? '')),
'tcaptcha_secret_key' => sanitize_text_field((string) ($s['tcaptcha_secret_key'] ?? '')),
'brush_limit_post' => [
'captcha' => $captcha,
'brush_limit_post' => [
'enabled' => !empty($s['brush_limit_post_enabled']),
'minutes_10' => (int) ($s['brush_limit_post_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_post_day_1'] ?? 50),
],
'brush_limit_comment' => [
'brush_limit_comment' => [
'enabled' => !empty($s['brush_limit_comment_enabled']),
'minutes_10' => (int) ($s['brush_limit_comment_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_comment_day_1'] ?? 100),
],
'brush_limit_upload' => [
'brush_limit_upload' => [
'enabled' => !empty($s['brush_limit_upload_enabled']),
'minutes_10' => (int) ($s['brush_limit_upload_minutes_10'] ?? 50),
'day_1' => (int) ($s['brush_limit_upload_day_1'] ?? 200),
],
'brush_limit_register' => [
'brush_limit_register' => [
'enabled' => !empty($s['brush_limit_register_enabled']),
'minutes_10' => (int) ($s['brush_limit_register_minutes_10'] ?? 20),
'day_1' => (int) ($s['brush_limit_register_day_1'] ?? 100),
],
'brush_limit_order' => [
'brush_limit_order' => [
'enabled' => !empty($s['brush_limit_order_enabled']),
'minutes_10' => (int) ($s['brush_limit_order_minutes_10'] ?? 30),
'day_1' => (int) ($s['brush_limit_order_day_1'] ?? 100),
],
'error_frequency_limit' => !empty($s['error_frequency_limit'] ?? true),
'user_disable_admin' => !empty($s['user_disable_admin'] ?? true),
'disabled_pingback' => !empty($s['disabled_pingback'] ?? true),
'disabled_xmlrpc' => !empty($s['disabled_xmlrpc'] ?? true),
'close_attachment_page' => !empty($s['close_attachment_page'] ?? true),
'error_frequency_limit' => !empty($s['error_frequency_limit'] ?? true),
];
}
/**
* 获取"系统工具"设置
*/