日志升级与缓存修复最终版

This commit is contained in:
root
2026-06-16 04:58:56 +08:00
parent bcb9f293a6
commit 6b3b99b0f1
61 changed files with 2405 additions and 333 deletions
+272
View File
@@ -0,0 +1,272 @@
<?php
require_once __DIR__ . '/ClientIpResolver.php';
require_once __DIR__ . '/GeoIpCountryCache.php';
require_once __DIR__ . '/GeoIpCountryResolver.php';
require_once __DIR__ . '/FpUrlHelper.php';
require_once __DIR__ . '/RiskSecondaryGate.php';
/**
* 客户端 SessionPHPSESSID)统一判定缓存:byApi 阶段缓存 + byApiRisk 最终缓存。
*/
class CloakJudgmentCache
{
const SESSION_KEY = 'cloak_judgment_cache';
const STAGE_BYAPI = 'byapi';
const STAGE_FINAL = 'final';
public static function hasSession()
{
return !empty($_SESSION[self::SESSION_KEY])
&& is_array($_SESSION[self::SESSION_KEY]);
}
/**
* @param string|null $currentIp
* @return array|null
*/
public static function get($currentIp = null)
{
if (!self::hasSession()) {
return null;
}
$cached = $_SESSION[self::SESSION_KEY];
if (!self::isConfigValid($cached)) {
self::clear();
$_SESSION['visit_to_3'] = '1';
return null;
}
$ip = ClientIpResolver::normalizeIp(
$currentIp !== null && $currentIp !== '' ? $currentIp : ClientIpResolver::resolve()
);
$cachedIp = ClientIpResolver::normalizeIp($cached['ip'] ?? '');
if ($ip !== null && $cachedIp !== null && $ip !== $cachedIp) {
self::refreshCountryForCurrentIp($ip);
$cached = $_SESSION[self::SESSION_KEY];
}
return $cached;
}
/**
* 二次风控关闭(CLOAK_RISK_NUMBER=0 等)时,byApi 阶段缓存即视为最终缓存。
*
* @param array $cached
*/
public static function isEffectivelyFinal(array $cached)
{
$stage = (string) ($cached['stage'] ?? '');
if ($stage === self::STAGE_FINAL) {
return true;
}
if ($stage === self::STAGE_BYAPI && !RiskSecondaryGate::isEnabled()) {
return true;
}
return false;
}
/**
* 仅返回最终缓存(可短路全流程)。
*
* @param string|null $currentIp
* @return array|null
*/
public static function getFinal($currentIp = null)
{
$cached = self::get($currentIp);
if ($cached === null || !self::isEffectivelyFinal($cached)) {
return null;
}
return $cached;
}
/**
* @param array|null $cached
*/
public static function isFinal($cached = null)
{
if ($cached === null) {
$cached = self::get();
}
return is_array($cached) && self::isEffectivelyFinal($cached);
}
/**
* @param array|null $cached
*/
public static function isPartial($cached = null)
{
if ($cached === null) {
$cached = self::get();
}
if (!is_array($cached) || ($cached['stage'] ?? '') !== self::STAGE_BYAPI) {
return false;
}
return RiskSecondaryGate::isEnabled();
}
/**
* byApi 完成后写入;按风险系数决定 stage。
* CLOAK_RISK_NUMBER=0(或未启用二次风控)时,byApi 结果直接记为 final。
*
* @param array{result:string,reason?:string,ip?:string,country?:string,fp_url?:string} $payload
*/
public static function storeFromByApi(array $payload)
{
$result = ($payload['result'] ?? '') === 'true' ? 'true' : 'false';
$stage = self::STAGE_FINAL;
if ($result === 'true' && RiskSecondaryGate::isEnabled()) {
$stage = self::STAGE_BYAPI;
}
self::writeCache($stage, $payload);
self::applyToSession($_SESSION[self::SESSION_KEY]);
}
/**
* byApiRisk 完成后写入最终缓存。
*
* @param array{result:string,reason?:string,fp_url?:string,ip?:string,country?:string} $payload
*/
public static function storeFromByApiRisk(array $payload)
{
self::writeCache(self::STAGE_FINAL, $payload);
self::applyToSession($_SESSION[self::SESSION_KEY]);
}
/**
* @param array $cached
*/
public static function applyToSession(array $cached)
{
$result = ($cached['result'] ?? '') === 'true' ? 'true' : 'false';
$_SESSION['check_result'] = $result;
$_SESSION['visit_to_2'] = '2';
if (self::isEffectivelyFinal($cached)) {
$_SESSION['visit_to_3'] = '3';
} else {
$_SESSION['visit_to_3'] = '1';
}
if (!empty($cached['ip'])) {
$_SESSION['gcu_ip'] = $cached['ip'];
}
if (array_key_exists('country', $cached)) {
$_SESSION['gcu_country'] = (string) $cached['country'];
}
}
/**
* @param object $v_info visitorInfo
* @param array $cached
*/
public static function applyToVisitorInfo($v_info, array $cached)
{
if (!is_object($v_info)) {
return;
}
if (!empty($cached['ip'])) {
$v_info->v_ip = $cached['ip'];
}
if (array_key_exists('country', $cached) && (string) $cached['country'] !== '') {
$v_info->v_country = (string) $cached['country'];
}
}
public static function clear()
{
unset($_SESSION[self::SESSION_KEY]);
}
public static function hitReason()
{
return '缓存';
}
/**
* @param array $cached
* @return array{result:bool,reason:string,link:string}
*/
public static function toApiResponse(array $cached)
{
$passed = ($cached['result'] ?? '') === 'true';
$link = (string) ($cached['fp_url'] ?? '');
if ($passed && $link === '') {
$link = FpUrlHelper::fallbackUrl();
}
if (!$passed && $link === '' && defined('DB_ZP')) {
$link = (string) DB_ZP;
}
return [
'result' => $passed,
'reason' => self::hitReason(),
'link' => $link,
];
}
/**
* @param string $stage
* @param array $payload
*/
private static function writeCache($stage, array $payload)
{
$ip = ClientIpResolver::normalizeIp($payload['ip'] ?? ClientIpResolver::resolve());
if ($ip === null) {
return;
}
$country = (string) ($payload['country'] ?? '');
if ($country === '' && !empty($_SESSION['gcu_country'])) {
$country = (string) $_SESSION['gcu_country'];
}
if ($country === '') {
$country = (string) (GeoIpCountryResolver::resolve($ip) ?? '');
}
$_SESSION[self::SESSION_KEY] = [
'stage' => $stage,
'ip' => $ip,
'country' => $country,
'result' => ($payload['result'] ?? '') === 'true' ? 'true' : 'false',
'reason' => (string) ($payload['reason'] ?? ''),
'fp_url' => (string) ($payload['fp_url'] ?? ''),
'risk_number' => defined('CLOAK_RISK_NUMBER') ? (int) CLOAK_RISK_NUMBER : 0,
'campaign_id' => defined('COSTM_IP_SCORE') ? (string) COSTM_IP_SCORE : '',
'cached_at' => time(),
];
}
/**
* @param string $currentIp
*/
private static function refreshCountryForCurrentIp($currentIp)
{
if (!self::hasSession()) {
return;
}
$cached = $_SESSION[self::SESSION_KEY];
$cached['ip'] = $currentIp;
$cached['country'] = (string) (GeoIpCountryResolver::resolve($currentIp) ?? '');
$_SESSION[self::SESSION_KEY] = $cached;
$_SESSION['gcu_ip'] = $currentIp;
$_SESSION['gcu_country'] = $cached['country'];
}
/**
* @param array $cached
*/
private static function isConfigValid(array $cached)
{
if (defined('CLOAK_RISK_NUMBER') && (int) ($cached['risk_number'] ?? -1) !== (int) CLOAK_RISK_NUMBER) {
return false;
}
if (defined('COSTM_IP_SCORE') && (string) ($cached['campaign_id'] ?? '') !== (string) COSTM_IP_SCORE) {
return false;
}
return true;
}
}
Regular → Executable
+13 -10
View File
@@ -18,7 +18,7 @@ class CloakSession
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => $domain,
'domain' => ltrim($domain, '.'),
'secure' => function_exists('cloak_request_is_https') && cloak_request_is_https(),
'httponly' => true,
'samesite' => 'Lax',
@@ -27,7 +27,7 @@ class CloakSession
session_set_cookie_params(
0,
'/',
$domain,
ltrim($domain, '.'),
function_exists('cloak_request_is_https') && cloak_request_is_https(),
true
);
@@ -80,7 +80,13 @@ class CloakFcheckCors
return false;
}
$requestHost = (string) ($_SERVER['HTTP_HOST'] ?? '');
$requestHost = function_exists('cloak_normalize_request_host')
? cloak_normalize_request_host($_SERVER['HTTP_HOST'] ?? '')
: strtolower(trim((string) ($_SERVER['HTTP_HOST'] ?? '')));
$originHost = function_exists('cloak_normalize_request_host')
? cloak_normalize_request_host($originHost)
: strtolower(trim($originHost));
if ($requestHost !== '' && strcasecmp($originHost, $requestHost) === 0) {
return true;
}
@@ -103,18 +109,15 @@ class CloakFcheckCors
*/
private static function registrableSuffix($host)
{
if (function_exists('cloak_registrable_domain')) {
return cloak_registrable_domain($host);
}
$host = strtolower(trim($host));
if ($host === '') {
return '';
}
if (function_exists('cloak_fingerprint_cookie_domain')) {
$cookieDomain = ltrim(cloak_fingerprint_cookie_domain(), '.');
if ($cookieDomain !== '' && self::hostMatchesSuffix($host, $cookieDomain)) {
return $cookieDomain;
}
}
$parts = explode('.', $host);
if (count($parts) < 2) {
return $host;
Regular → Executable
+47 -43
View File
@@ -6,7 +6,11 @@ require_once dirname(__DIR__) . '/DbHelper.php';
require_once __DIR__ . '/FpUrlHelper.php';
require_once __DIR__ . '/VisitorApiAudit.php';
require_once __DIR__ . '/RiskSecondaryGate.php';
require_once __DIR__ . '/CloakJudgmentCache.php';
require_once __DIR__ . '/RiskSecondaryCache.php';
require_once __DIR__ . '/VisitorVisitContext.php';
require_once __DIR__ . '/ReasonHelper.php';
require_once __DIR__ . '/VisitorRepository.php';
require_once __DIR__ . '/VisitorLogSchema.php';
require_once __DIR__ . '/ClientIpResolver.php';
@@ -46,6 +50,9 @@ class FCheckHandler
}
$config_name = ConfigLoader::loadByHost($fp_host);
$log_id = (int) ($fingerprint_info['log_id'] ?? 0);
if ($log_id <= 0) {
$log_id = VisitorVisitContext::currentLogId();
}
if (!RiskSecondaryGate::isEnabled()) {
$_SESSION['visit_to_3'] = 3;
@@ -62,9 +69,9 @@ class FCheckHandler
if ($log_id > 0) {
self::updateLog($log_id, [
'result' => 'true',
'reason' => '二次风控已关闭',
'reason' => '',
'fp_url' => $response['link'],
], $fingerprint_info, null, null);
], $fingerprint_info, null, null, false);
}
return array_merge(['ok' => true, 'status' => 200, 'fingerprint_info' => $fingerprint_info], $response);
@@ -163,7 +170,7 @@ class FCheckHandler
self::persistCache($update_log, $clientIp);
if ($log_id > 0) {
self::updateLog($log_id, $update_log, $fingerprint_info, $jsonData, $return);
self::updateLog($log_id, $update_log, $fingerprint_info, $jsonData, $return, true);
}
return array_merge([
@@ -186,7 +193,7 @@ class FCheckHandler
$update_log = [
'result' => $response['result'] ? 'true' : 'false',
'reason' => $response['reason'],
'reason' => cloak_cache_hit_log_reason($update_log['result']),
'fp_url' => trim((string) ($response['link'] ?? '')),
];
if ($update_log['fp_url'] === '') {
@@ -196,14 +203,7 @@ class FCheckHandler
}
if ($log_id > 0) {
$cacheMarker = [
'cached' => true,
'result' => $update_log['result'],
'reason' => $cached['reason'] ?? '',
'api_reason' => $cached['reason'] ?? '',
'cached_at' => $cached['cached_at'] ?? null,
];
self::updateLog($log_id, $update_log, $fingerprint_info, null, $cacheMarker);
self::updateLog($log_id, $update_log, $fingerprint_info, null, null, false);
}
return array_merge([
@@ -222,7 +222,10 @@ class FCheckHandler
{
$update_log = [];
$update_log['result'] = !empty($response['result']) ? 'true' : 'false';
$update_log['reason'] = $response['reason'] ?? '';
$update_log['reason'] = cloak_normalize_log_reason(
$update_log['result'],
$response['reason'] ?? ''
);
$update_log['fp_url'] = trim((string) ($response['link'] ?? ''));
if ($update_log['fp_url'] === '') {
$update_log['fp_url'] = $update_log['result'] === 'true'
@@ -239,11 +242,12 @@ class FCheckHandler
*/
private static function persistCache(array $update_log, $clientIp)
{
RiskSecondaryCache::store([
'ip' => $clientIp,
'result' => $update_log['result'],
'reason' => $update_log['reason'],
'fp_url' => $update_log['fp_url'],
CloakJudgmentCache::storeFromByApiRisk([
'ip' => $clientIp,
'country' => (string) ($_SESSION['gcu_country'] ?? ''),
'result' => $update_log['result'],
'reason' => $update_log['reason'],
'fp_url' => $update_log['fp_url'],
]);
}
@@ -253,36 +257,36 @@ class FCheckHandler
* @param array $fingerprint_info
* @param array|null $apiRequest
* @param array|null $apiResponse
* @param bool $includeFpAndApi 缓存路径不写 fp_hdata / api_risk_*
*/
public static function updateLog($log_id, array $update_log, array $fingerprint_info, $apiRequest, $apiResponse)
public static function updateLog($log_id, array $update_log, array $fingerprint_info, $apiRequest, $apiResponse, $includeFpAndApi = true)
{
$conn = @new mysqli('localhost', DB_USERNAME, DB_PASSWORD, DB_NAME);
if (!$conn || $conn->connect_error) {
return false;
}
VisitorLogSchema::ensureColumns($conn);
$payload = [
'result' => (string) ($update_log['result'] ?? ''),
'reason' => cloak_normalize_log_reason(
$update_log['result'] ?? '',
$update_log['reason'] ?? ''
),
'fp_url' => (string) ($update_log['fp_url'] ?? ''),
];
$resultEsc = cloak_db_escape($conn, $update_log['result'] ?? '');
$reasonEsc = cloak_db_escape($conn, strip_tags(cloak_db_string($update_log['reason'] ?? '')));
$fpUrlEsc = cloak_db_escape($conn, strip_tags(cloak_db_string($update_log['fp_url'] ?? '')));
$fpHdataJson = json_encode($fingerprint_info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($fpHdataJson === false) {
$fpHdataJson = '';
}
$fpHdataEsc = cloak_db_escape($conn, $fpHdataJson);
$apiRiskReqEsc = cloak_db_escape($conn, $apiRequest !== null ? VisitorApiAudit::encode($apiRequest) : '');
$apiRiskRespEsc = cloak_db_escape($conn, $apiResponse !== null ? VisitorApiAudit::encode($apiResponse) : '');
$sql = "UPDATE `visitor_logs` SET `result`='{$resultEsc}',`reason`='{$reasonEsc}',`fp_url`='{$fpUrlEsc}',`fp_hdata`='{$fpHdataEsc}',`api_risk_request`='{$apiRiskReqEsc}',`api_risk_response`='{$apiRiskRespEsc}' WHERE `id`='" . (int) $log_id . "'";
$ok = $conn->query($sql) === true;
if (!$ok) {
$handle = @fopen(dirname(__DIR__, 2) . '/err.txt', 'a+');
if ($handle) {
fwrite($handle, date('Y-m-d H:i:s') . ' | FCheckHandler update: ' . $conn->error . "\n");
fclose($handle);
if ($includeFpAndApi) {
$fpHdataJson = json_encode($fingerprint_info, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if ($fpHdataJson === false) {
$fpHdataJson = '';
}
$payload['fp_hdata'] = $fpHdataJson;
$payload['api_risk_request'] = $apiRequest !== null ? VisitorApiAudit::encode($apiRequest) : '';
$payload['api_risk_response'] = $apiResponse !== null ? VisitorApiAudit::encode($apiResponse) : '';
}
$conn->close();
$hadForceSync = !empty($GLOBALS['__cloak_force_sync_log']);
$GLOBALS['__cloak_force_sync_log'] = true;
$ok = cloak_update_log_db((int) $log_id, $payload, ['skip_enrich' => true]) !== false;
if (!$hadForceSync) {
unset($GLOBALS['__cloak_force_sync_log']);
}
return $ok;
}
}
+6 -4
View File
@@ -7,7 +7,7 @@ require_once __DIR__ . '/../FpUrlHelper.php';
class FpPageRenderer
{
/**
* @param array $ctx 必须包含 logs 数组
* @param array $ctx 必须包含 logs 数组;可选 skip_log
*/
public static function render(array $ctx)
{
@@ -17,10 +17,12 @@ class FpPageRenderer
$my_file_name = substr($my_url, strrpos($my_url, '/') + 1);
$cong_name = str_replace('.php', '', $my_file_name);
$resolved = FpUrlHelper::resolve($cong_name, true);
$fp_url = $resolved['url'];
$resolved = FpUrlHelper::resolve($cong_name, true);
$fp_url = $resolved['url'];
$logs['fp_url'] = $fp_url;
write_log_db($logs);
if (empty($ctx['skip_log'])) {
write_log_db($logs);
}
$show_content = CLOAK_SHOW_CONTENT;
if ($show_content == 'curl') {
+160 -20
View File
@@ -1,16 +1,21 @@
<?php
require_once __DIR__ . '/../WhitelistGate.php';
require_once __DIR__ . '/../CloakJudgmentCache.php';
require_once __DIR__ . '/../RiskSecondaryGate.php';
/**
* 流量判定主流水线
*
* 阶段顺序(不可调换):
* 1. Session 快速路径 → 2. GeoIP 国家 → 3. 黑白名单 → 4. 广告来源 → 5. 临时链接 → 6. API/指纹
* 优先级(高 → 低):
* 1. 白名单链接参数 WHITE_PARAMS
* 2. 白名单 IP
* 3. 黑名单
* 4. 屏蔽模式:安全页(zp) / 真实页(fp)
* 5. 正常屏蔽(ip_check)Session 最终缓存 > 阶段缓存续跑 > 完整判定
*/
class CheckPipeline
{
/**
* 在调用方(ip_check 顶层)作用域执行完整判定链。
* 依赖全局:$v_info, $config_name, $__cloak_debug_on, $reason
*
* @return void
*/
public static function run()
@@ -23,15 +28,64 @@ class CheckPipeline
CloakPipelineTimer::init();
CloakPipelineTimer::stage('Session快速路径');
if (self::resolveSessionFastPath()) {
CloakPipelineTimer::stage('白名单链接参数');
if (self::resolveWhitelistParamsFastPath()) {
include __DIR__ . '/stages/resolve_whitelist_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('白名单IP');
if (self::resolveWhitelistIpFastPath()) {
include __DIR__ . '/stages/resolve_whitelist_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('黑名单');
if (self::resolveBlacklistFastPath()) {
include __DIR__ . '/stages/resolve_blacklist_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('安全页模式');
if (self::resolveZpFastPath()) {
include __DIR__ . '/stages/resolve_zp_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('真实页模式');
if (self::resolveFpFastPath()) {
include __DIR__ . '/stages/resolve_fp_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('客户端缓存快速路径');
if (self::resolveClientCacheFastPath()) {
include __DIR__ . '/stages/resolve_session_fast_path_hit.inc.php';
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('客户端阶段缓存续跑');
if (self::resolvePartialCacheResume()) {
include __DIR__ . '/stages/run_main_api_fingerprint.inc.php';
CloakPipelineTimer::finish();
return;
}
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, 'Session 快速路径', 'skip', '未命中缓存,进入完整判定流程(DEBUG 已重置 visit_to_*');
cloak_dbg_step(__LINE__, '完整判定', 'info', '未命中早期快速路径,进入完整判定');
}
CloakPipelineTimer::stage('黑白名单');
include __DIR__ . '/stages/apply_whitelist_blacklist.inc.php';
if (!empty($_SESSION['check_result'])) {
CloakPipelineTimer::finish();
return;
}
CloakPipelineTimer::stage('GeoIP国家判定');
@@ -41,8 +95,6 @@ class CheckPipeline
return;
}
CloakPipelineTimer::stage('黑白名单');
include __DIR__ . '/stages/apply_whitelist_blacklist.inc.php';
CloakPipelineTimer::stage('广告来源限制');
include __DIR__ . '/stages/run_ad_source_guard.inc.php';
CloakPipelineTimer::stage('临时链接参数');
@@ -51,20 +103,108 @@ class CheckPipeline
CloakPipelineTimer::finish();
}
public static function resolveWhitelistParamsFastPath()
{
return WhitelistGate::matchesWhitelistParams();
}
public static function resolveWhitelistIpFastPath()
{
global $v_info;
if (WhitelistGate::matchesWhitelistParams()) {
return false;
}
return WhitelistGate::matchesWhitelistIp(WhitelistGate::resolveVisitorIp($v_info));
}
public static function resolveBlacklistFastPath()
{
global $v_info;
if (!defined('BLACKLIST_GROUP_ID') || (int) BLACKLIST_GROUP_ID <= 0) {
return false;
}
$blEntries = ip_group_list_addresses((int) BLACKLIST_GROUP_ID);
return ip_visitor_in_blacklist_entries($v_info->v_ip, $blEntries);
}
public static function resolveClientCacheFastPath()
{
global $__cloak_debug_on, $v_info;
if ($__cloak_debug_on || SHOW_SITE_MODE_SWITCH !== 'ip_check') {
return false;
}
$cached = CloakJudgmentCache::getFinal();
if ($cached === null) {
return false;
}
CloakJudgmentCache::applyToSession($cached);
CloakJudgmentCache::applyToVisitorInfo($v_info, $cached);
if ($__cloak_debug_on) {
CloakDebugPage::setFlag('session_cached', true);
}
return true;
}
public static function resolvePartialCacheResume()
{
global $__cloak_debug_on, $v_info, $reason;
if ($__cloak_debug_on || SHOW_SITE_MODE_SWITCH !== 'ip_check') {
return false;
}
$cached = CloakJudgmentCache::get();
if (!CloakJudgmentCache::isPartial($cached)) {
return false;
}
CloakJudgmentCache::applyToSession($cached);
CloakJudgmentCache::applyToVisitorInfo($v_info, $cached);
$_SESSION['visit_to_2'] = '2';
if ($reason === '') {
$reason = 'Session byApi阶段缓存:待二次风控';
}
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '客户端阶段缓存续跑', 'pass', '命中 byapi 阶段缓存,跳过 GeoIP/Guard/byApi');
}
return true;
}
/**
* 是否命中 session 缓存结果(true = 跳过完整判定)
*
* @return bool
* @deprecated
*/
public static function resolveSessionFastPath()
{
global $__cloak_debug_on;
return self::resolveClientCacheFastPath();
}
return (
!$__cloak_debug_on
&& SHOW_SITE_MODE_SWITCH === 'ip_check'
&& isset($_SESSION['check_result'])
&& ($_SESSION['check_result'] === 'true' || $_SESSION['check_result'] === 'false')
);
/**
* @deprecated 使用 resolveWhitelistParamsFastPath / resolveWhitelistIpFastPath
*/
public static function resolveWhitelistFastPath()
{
return self::resolveWhitelistParamsFastPath() || self::resolveWhitelistIpFastPath();
}
public static function resolveZpFastPath()
{
return defined('SHOW_SITE_MODE_SWITCH') && SHOW_SITE_MODE_SWITCH === 'zp';
}
public static function resolveFpFastPath()
{
return defined('SHOW_SITE_MODE_SWITCH') && SHOW_SITE_MODE_SWITCH === 'fp';
}
}
+47 -11
View File
@@ -1,9 +1,11 @@
<?php
require_once __DIR__ . '/../WhitelistGate.php';
require_once __DIR__ . '/../Page/FpPageRenderer.php';
require_once __DIR__ . '/../Page/RiskPageRenderer.php';
require_once __DIR__ . '/../FpUrlHelper.php';
require_once __DIR__ . '/../RiskSecondaryGate.php';
require_once __DIR__ . '/../RiskSecondaryCache.php';
require_once __DIR__ . '/../RiskSecondarySession.php';
require_once __DIR__ . '/../ClientIpResolver.php';
/**
@@ -18,6 +20,12 @@ class RouteResolver
{
extract($ctx, EXTR_SKIP);
// 白名单优先于正品模式:跳过二次风控等,直达真实页
if (!empty($_SESSION['cloak_whitelist_fast'])) {
self::renderWhitelistFp($logs, $config_name ?? '');
return;
}
// 正品模式:始终走安全页逻辑
if (SHOW_SITE_MODE_SWITCH == 'zp') {
self::renderSafePage($logs, $v_info);
@@ -28,13 +36,26 @@ class RouteResolver
|| SHOW_SITE_MODE_SWITCH == 'fp';
if ($showFp) {
self::renderFpOrRisk($logs, $v_info);
self::renderFpOrRisk($logs, $v_info, $config_name ?? '');
return;
}
self::renderSafePage($logs, $v_info);
}
/**
* 白名单快速路径:写日志并输出真实页,不触发二次风控 / API
*
* @param array $logs
* @param string $config_name
*/
private static function renderWhitelistFp(array $logs, $config_name = '')
{
$logs['result'] = 'true';
$logs['reason'] = '';
self::renderFpPage($logs, $config_name);
}
/**
* @param array $logs
*/
@@ -69,7 +90,7 @@ class RouteResolver
}
}
private static function renderFpOrRisk(array $logs, $v_info)
private static function renderFpOrRisk(array $logs, $v_info, $config_name = '')
{
if (AUTO_BLACK == 'ON') {
$times = howmanytimesbyip($v_info->v_ip);
@@ -81,21 +102,22 @@ class RouteResolver
$_SESSION['visit_to_3'] = '1';
}
if (RiskSecondaryGate::isEnabled()) {
if (RiskSecondaryGate::isEnabledForRequest()) {
$cached = RiskSecondaryCache::get(ClientIpResolver::resolve());
if ($cached !== null) {
self::renderFromRiskCache($logs, $v_info, $cached);
self::renderFromRiskCache($logs, $v_info, $cached, $config_name);
return;
}
}
if (($_SESSION['visit_to_3'] ?? '1') != '3'
&& RiskSecondaryGate::isEnabled()) {
&& RiskSecondaryGate::isEnabledForRequest()) {
// 二次风控:先写 wait 状态,f_check 完成后再更新 result / fp_url(避免 true+空跳转)
$logs['result'] = 'wait';
$logs['reason'] = '二次风控检测中';
$logs['fp_url'] = '';
$log_id = write_log_db($logs);
RiskSecondarySession::markWaitLog($log_id);
$riskCtx = [
'logs' => $logs,
'v_info' => $v_info,
@@ -111,22 +133,36 @@ class RouteResolver
$logs['result'] = 'true';
$logs['reason'] = '';
FpPageRenderer::render(['logs' => $logs]);
self::renderFpPage($logs, $config_name);
}
/**
* @param array $logs
* @param string $config_name
*/
private static function renderFpPage(array $logs, $config_name = '')
{
FpPageRenderer::render([
'logs' => $logs,
'config_name' => $config_name,
'skip_log' => RiskSecondarySession::shouldSkipFpPageLog(),
]);
}
/**
* 二次风控 Session 缓存命中:跳过 cloakjs / byApiRisk。
*
* @param array $logs
* @param array $cached
* @param array $logs
* @param array $cached
* @param string $config_name
*/
private static function renderFromRiskCache(array $logs, $v_info, array $cached)
private static function renderFromRiskCache(array $logs, $v_info, array $cached, $config_name = '')
{
RiskSecondaryCache::applyToSession($cached);
$apiResponse = RiskSecondaryCache::toApiResponse($cached);
$logs['result'] = $apiResponse['result'] ? 'true' : 'false';
$logs['reason'] = $apiResponse['reason'];
$logs['reason'] = cloak_cache_hit_log_reason($logs['result']);
$logs['fp_url'] = trim((string) ($apiResponse['link'] ?? ''));
if ($logs['fp_url'] === '') {
$logs['fp_url'] = $logs['result'] === 'true'
@@ -139,7 +175,7 @@ class RouteResolver
return;
}
FpPageRenderer::render(['logs' => $logs]);
self::renderFpPage($logs, $config_name);
}
/**
@@ -0,0 +1,30 @@
<?php
/**
* 仅用本地 MaxMind 解析国家并写入访客信息/Session(不做国家允许列表或其它判定)
*
* 依赖全局:$v_info, $__cloak_debug_on
*/
$ip = ClientIpResolver::resolve();
if ($ip !== '' && $ip !== '0.0.0.0') {
$v_info->v_ip = $ip;
$_SESSION['gcu_ip'] = $ip;
}
if (GeoIpCountryResolver::isAvailable()) {
$geoMeta = GeoIpCountryResolver::resolveWithMeta($ip);
$countryCode = $geoMeta['code'];
$geoSource = $geoMeta['source'];
if ($countryCode !== null && $countryCode !== '') {
$v_info->v_country = $countryCode;
$_SESSION['gcu_country'] = $countryCode;
}
if (!empty($GLOBALS['__cloak_debug_on'])) {
cloak_dbg_step(__LINE__, 'MaxMind国家记录', 'log', '仅记录国家,不参与判定', [
'ip' => $ip,
'country' => $countryCode,
'geo_source' => $geoSource,
]);
}
} elseif (!empty($GLOBALS['__cloak_debug_on'])) {
cloak_dbg_step(__LINE__, 'MaxMind国家记录', 'skip', '本地 GeoIP 库不可用');
}
@@ -1,69 +1,46 @@
<?php
//指定ip显示仿品(优先数据库白名单组,其次兼容旧版 SHOW_SITE_IP 逗号列表)
if (defined('WHITELIST_GROUP_ID') && (int) WHITELIST_GROUP_ID > 0) {
$ips_fp_ary = ip_group_list_addresses((int) WHITELIST_GROUP_ID);
} else {
$ips_fp_ary = array_filter(array_map('trim', explode(',', SHOW_SITE_IP)), static function ($v) {
return $v !== '';
});
}
$reason = ""; // 判断理由
$whiteParamsMatched = false;
if( !empty(WHITE_PARAMS) ) {
$wparams = explode('=', WHITE_PARAMS, 2);
if(count($wparams) == 2) {
$pk = $wparams[0];
$pv = $wparams[1];
}
if(isset($_GET[$pk]) && strip_tags($_GET[$pk]) == $pv) {
$whiteParamsMatched = true;
// 黑白名单(完整判定链内):链接参数白名单 > IP 白名单 > 黑名单
$reason = $reason ?? '';
if (WhitelistGate::matchesWhitelistParams()) {
$_SESSION['check_result'] = 'true';
$reason = '白名单链接参数';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '链接参数白名单', 'pass', defined('WHITE_PARAMS') ? WHITE_PARAMS : '');
cloak_dbg_record(__LINE__, 'true', $reason, ['stage' => 'whitelist_params']);
}
return;
}
if (WhitelistGate::matchesWhitelistIp(WhitelistGate::resolveVisitorIp($v_info))) {
$_SESSION['check_result'] = 'true';
$reason = '白名单';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, 'IP 白名单', 'pass', 'IP 在白名单组/列表中');
cloak_dbg_record(__LINE__, 'true', $reason, ['stage' => 'whitelist_ip']);
}
return;
}
if ($__cloak_debug_on && defined('WHITE_PARAMS') && WHITE_PARAMS !== '') {
cloak_dbg_step(__LINE__, '链接参数白名单', 'skip', '参数不匹配: ' . WHITE_PARAMS);
}
$match = false;
if (defined('BLACKLIST_GROUP_ID') && (int) BLACKLIST_GROUP_ID > 0) {
$blEntries = ip_group_list_addresses((int) BLACKLIST_GROUP_ID);
$match = ip_visitor_in_blacklist_entries($v_info->v_ip, $blEntries);
$match = ip_visitor_in_blacklist_entries($v_info->v_ip, $blEntries);
}
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__,'黑名单检查', $match ? 'fail' : 'pass', $match ? 'IP 在黑名单组内' : '未命中黑名单', [
'ip' => $v_info->v_ip,
cloak_dbg_step(__LINE__, '黑名单检查', $match ? 'fail' : 'pass', $match ? 'IP 在黑名单组内' : '未命中黑名单', [
'ip' => $v_info->v_ip,
'group_id' => defined('BLACKLIST_GROUP_ID') ? BLACKLIST_GROUP_ID : 0,
]);
}
if ($match) {
$_SESSION["check_result"] = "false";
$reason = "黑名单";
$_SESSION['check_result'] = 'false';
$reason = '黑名单';
if ($__cloak_debug_on) {
cloak_dbg_record(__LINE__,'false', $reason, ['stage' => 'blacklist']);
cloak_dbg_record(__LINE__, 'false', $reason, ['stage' => 'blacklist']);
}
}
if(in_array($v_info->v_ip, $ips_fp_ary) ) {
$_SESSION["check_result"] = "true";
$reason = "白名单";
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__,'IP 白名单', 'pass', 'IP 在白名单组/列表中');
cloak_dbg_record(__LINE__,'true', $reason, ['stage' => 'whitelist_ip']);
}
} elseif( !empty(WHITE_PARAMS) ) {
$wparams = explode('=', WHITE_PARAMS, 2);
if(count($wparams) == 2) {
$pk = $wparams[0];
$pv = $wparams[1];
}
if(isset($_GET[$pk]) && strip_tags($_GET[$pk]) == $pv) {
$_SESSION["check_result"] = "true";
$reason = "白名单链接参数";
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__,'链接参数白名单', 'pass', WHITE_PARAMS);
cloak_dbg_record(__LINE__,'true', $reason, ['stage' => 'whitelist_params']);
}
}
} elseif ($__cloak_debug_on && !empty(WHITE_PARAMS)) {
cloak_dbg_step(__LINE__,'链接参数白名单', 'skip', '参数不匹配: ' . WHITE_PARAMS);
}
@@ -0,0 +1,15 @@
<?php
/**
* 黑名单快速路径:记录国家后直达安全页判定
*/
include __DIR__ . '/apply_geoip_country_log_only.inc.php';
global $reason;
$_SESSION['check_result'] = 'false';
$reason = '黑名单';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '黑名单快速路径', 'fail', 'IP 在黑名单组内');
cloak_dbg_record(__LINE__, 'false', $reason, ['stage' => 'blacklist_fast_path']);
}
+17
View File
@@ -0,0 +1,17 @@
<?php
/**
* 真实页模式(fp)快速路径:跳过正常屏蔽判定与二次风控,直达真实页
*/
include __DIR__ . '/apply_geoip_country_log_only.inc.php';
global $reason;
$_SESSION['check_result'] = 'true';
$reason = '真实页模式';
$_SESSION['visit_to_2'] = '2';
$_SESSION['visit_to_3'] = '3';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '真实页模式', 'pass', '跳过正常屏蔽判定链');
cloak_dbg_record(__LINE__, 'true', $reason, ['stage' => 'fp_fast_path']);
}
@@ -0,0 +1,17 @@
<?php
/**
* 白名单快速路径:仅 MaxMind 国家记录,跳过其余判定,直达真实页
*/
include __DIR__ . '/apply_geoip_country_log_only.inc.php';
global $reason;
$_SESSION['check_result'] = 'true';
$reason = WhitelistGate::matchReason($v_info);
$_SESSION['visit_to_3'] = '3';
$_SESSION['cloak_whitelist_fast'] = true;
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '白名单快速路径', 'pass', $reason);
cloak_dbg_record(__LINE__, 'true', $reason, ['stage' => 'whitelist_fast_path']);
}
+15
View File
@@ -0,0 +1,15 @@
<?php
/**
* 正品模式快速路径:仅 MaxMind 国家记录,跳过其余判定,无条件安全页
*/
include __DIR__ . '/apply_geoip_country_log_only.inc.php';
global $reason;
$_SESSION['check_result'] = 'false';
$reason = '正品模式';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, '正品模式', 'pass', '仅记录国家,无条件安全页');
cloak_dbg_record(__LINE__, 'false', $reason, ['stage' => 'zp_fast_path']);
}
@@ -22,6 +22,10 @@
}
}
if (($_SESSION['visit_to_2'] ?? '') == '2' && class_exists('CloakJudgmentCache', false) && !CloakJudgmentCache::hasSession()) {
$_SESSION['visit_to_2'] = '1';
}
if($_SESSION['visit_to_1'] == '1' && $_SESSION['visit_to_2'] != '2') // $_SESSION['visit_to_2'] 为2时表示已经判断过所有规则。
{
if ($__cloak_debug_on) {
@@ -86,7 +90,7 @@
// 已完成指纹跳转(URL 含 time),但 Cookie 未写入:终止再次重定向
$v_info->check_result = 'false';
$reason = cloak_reason_or($reason, '指纹Cookie未写入(已完成前端检测)');
$reason = cloak_reason_or($reason, '已完成前端检测,但指纹Cookie未写入(隐私模式、广告拦截等可能阻止Cookie写入)');
$_SESSION['check_result'] = 'false';
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__, 'JS 指纹检测', 'fail', 'URL 含 time 但指纹 Cookie 缺失,停止重定向');
@@ -149,6 +153,14 @@
if ($__api_return !== null) {
$_SESSION["check_result"] = trim($v_info->check_result);
$reason = cloak_api_reason($__api_return, $_SESSION["check_result"]);
if (class_exists('CloakJudgmentCache', false)) {
CloakJudgmentCache::storeFromByApi([
'ip' => $v_info->v_ip,
'country' => $v_info->v_country,
'result' => $_SESSION['check_result'],
'reason' => $reason,
]);
}
} else {
$_SESSION["check_result"] = "false";
$reason = cloak_reason_or($reason, 'API未调用:缺少访客信息(ip/Browser/Accept-Language)');
@@ -175,15 +187,33 @@
}
}
} elseif (($_SESSION['visit_to_2'] ?? '') == '2') {
if (!empty($_SESSION["check_result"])) {
$cached = class_exists('CloakJudgmentCache', false) ? CloakJudgmentCache::get() : null;
if ($cached !== null) {
CloakJudgmentCache::applyToSession($cached);
CloakJudgmentCache::applyToVisitorInfo($v_info, $cached);
$_SESSION['check_result'] = trim((string) ($cached['result'] ?? 'false'));
if ($reason === '') {
if (CloakJudgmentCache::isPartial($cached)) {
$reason = 'Session byApi阶段缓存:待二次风控';
} else {
$reason = ($_SESSION['check_result'] === 'true')
? 'Session API缓存:判定通过'
: '缓存';
}
}
} elseif (!empty($_SESSION["check_result"])) {
if ($reason === '') {
$reason = ($_SESSION['check_result'] === 'true')
? 'Session API缓存:判定通过'
: 'Session API缓存:判定拒绝';
: '缓存';
}
} else {
$_SESSION["check_result"] = 'false';
$_SESSION['check_result'] = 'false';
$reason = cloak_reason_or($reason, 'Session状态异常(visit_to_2=2但无缓存结果),默认安全页');
$_SESSION['visit_to_2'] = '1';
if (class_exists('CloakJudgmentCache', false)) {
CloakJudgmentCache::clear();
}
}
if ($__cloak_debug_on) {
cloak_dbg_step(__LINE__,'主判定块 (API)', 'skip', 'visit_to_2=2,本块已跳过(' . $reason . '');
+39 -6
View File
@@ -41,14 +41,47 @@ if (!function_exists('cloak_api_reason')) {
}
}
if (!function_exists('cloak_cache_hit_log_reason')) {
/**
* 缓存判定落库 reasontrue 为空;false 固定为「缓存」。
*
* @param string $result
*/
function cloak_cache_hit_log_reason($result)
{
return cloak_normalize_log_reason((string) $result, (string) $result === 'true' ? '' : '缓存');
}
}
if (!function_exists('cloak_normalize_log_reason')) {
/**
* 落库 reason 规范化:true 必须为空;false 必须有值;wait 保留检测中文案。
*
* @param string $result
* @param mixed $reason
*/
function cloak_normalize_log_reason($result, $reason)
{
$result = (string) $result;
$reason = trim((string) $reason);
if ($result === 'true') {
return '';
}
if ($result === 'wait') {
return $reason !== '' ? $reason : '二次风控检测中';
}
if ($reason === '') {
return '判定拒绝(安全页)';
}
return $reason;
}
}
if (!function_exists('cloak_finalize_reason')) {
function cloak_finalize_reason($reason, $checkResult)
{
$reason = trim((string) $reason);
if ($reason !== '') {
return $reason;
}
$pass = ($checkResult === 'true');
return $pass ? '判定通过(真实页)' : '判定拒绝(安全页)';
$result = ($checkResult === 'true') ? 'true' : (($checkResult === 'false') ? 'false' : (string) $checkResult);
return cloak_normalize_log_reason($result, $reason);
}
}
Regular → Executable
+13 -65
View File
@@ -1,86 +1,48 @@
<?php
require_once __DIR__ . '/ClientIpResolver.php';
require_once __DIR__ . '/FpUrlHelper.php';
require_once __DIR__ . '/CloakJudgmentCache.php';
/**
* 二次风控byApiRiskSession 缓存:同 Session 内复用判定结果,IP 变化时失效
* 二次风控缓存薄封装(委托 CloakJudgmentCache,保持既有调用点兼容)
*/
class RiskSecondaryCache
{
const SESSION_KEY = 'cloak_risk_cache';
const SESSION_KEY = 'cloak_judgment_cache';
/**
* @param array{result:string,reason?:string,fp_url?:string,ip?:string} $payload
* @param array{result:string,reason?:string,fp_url?:string,ip?:string,country?:string} $payload
*/
public static function store(array $payload)
{
$ip = ClientIpResolver::normalizeIp($payload['ip'] ?? ClientIpResolver::resolve());
if ($ip === null) {
return;
}
$_SESSION[self::SESSION_KEY] = [
'ip' => $ip,
'result' => ($payload['result'] ?? '') === 'true' ? 'true' : 'false',
'reason' => (string) ($payload['reason'] ?? ''),
'fp_url' => (string) ($payload['fp_url'] ?? ''),
'risk_number' => defined('CLOAK_RISK_NUMBER') ? (int) CLOAK_RISK_NUMBER : 0,
'campaign_id' => defined('COSTM_IP_SCORE') ? (string) COSTM_IP_SCORE : '',
'cached_at' => time(),
];
CloakJudgmentCache::storeFromByApiRisk($payload);
}
/**
* @param string|null $currentIp 为空时使用 ClientIpResolver::resolve()
* 仅返回最终缓存(partial 不视为命中)。
*
* @param string|null $currentIp
* @return array|null
*/
public static function get($currentIp = null)
{
if (empty($_SESSION[self::SESSION_KEY]) || !is_array($_SESSION[self::SESSION_KEY])) {
return null;
}
$cached = $_SESSION[self::SESSION_KEY];
$ip = ClientIpResolver::normalizeIp($currentIp !== null && $currentIp !== '' ? $currentIp : ClientIpResolver::resolve());
$cachedIp = ClientIpResolver::normalizeIp($cached['ip'] ?? '');
$invalidate = false;
if ($ip === null || $cachedIp === null || $ip !== $cachedIp) {
$invalidate = true;
} elseif (defined('CLOAK_RISK_NUMBER') && (int) ($cached['risk_number'] ?? -1) !== (int) CLOAK_RISK_NUMBER) {
$invalidate = true;
} elseif (defined('COSTM_IP_SCORE') && (string) ($cached['campaign_id'] ?? '') !== (string) COSTM_IP_SCORE) {
$invalidate = true;
}
if ($invalidate) {
self::clear();
$_SESSION['visit_to_3'] = '1';
return null;
}
return $cached;
return CloakJudgmentCache::getFinal($currentIp);
}
public static function clear()
{
unset($_SESSION[self::SESSION_KEY]);
CloakJudgmentCache::clear();
}
/**
* 将缓存同步到 visit_to_3 / check_result,供 RouteResolver 等复用。
*
* @param array $cached
*/
public static function applyToSession(array $cached)
{
$_SESSION['visit_to_3'] = 3;
$_SESSION['check_result'] = ($cached['result'] ?? '') === 'true' ? 'true' : 'false';
CloakJudgmentCache::applyToSession($cached);
}
public static function hitReason()
{
return '二次风控缓存命中';
return CloakJudgmentCache::hitReason();
}
/**
@@ -89,20 +51,6 @@ class RiskSecondaryCache
*/
public static function toApiResponse(array $cached)
{
$passed = ($cached['result'] ?? '') === 'true';
$link = (string) ($cached['fp_url'] ?? '');
if ($passed && $link === '') {
$link = FpUrlHelper::fallbackUrl();
}
if (!$passed && $link === '' && defined('DB_ZP')) {
$link = (string) DB_ZP;
}
return [
'result' => $passed,
'reason' => self::hitReason(),
'link' => $link,
];
return CloakJudgmentCache::toApiResponse($cached);
}
}
+11
View File
@@ -12,4 +12,15 @@ class RiskSecondaryGate
$riskNumber = (int) CLOAK_RISK_NUMBER;
return $riskNumber > 0 && $riskNumber <= 90;
}
/**
* 当前站点模式是否应执行二次风控(真实页 fp 模式永不触发)。
*/
public static function isEnabledForRequest()
{
if (defined('SHOW_SITE_MODE_SWITCH') && SHOW_SITE_MODE_SWITCH === 'fp') {
return false;
}
return self::isEnabled();
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
require_once __DIR__ . '/VisitorVisitContext.php';
/**
* 二次风控 wait / f_check 与 visit 日志绑定(与 VisitorVisitContext 对齐)
*/
class RiskSecondarySession
{
const KEY_WAIT_LOG = 'cloak_risk_wait_log_id';
/**
* @param int|null $logId
*/
public static function markWaitLog($logId)
{
$logId = (int) $logId;
if ($logId > 0) {
$_SESSION[self::KEY_WAIT_LOG] = $logId;
VisitorVisitContext::bindLogId($logId);
}
}
/**
* f_check 已 finalize 后,FpPageRenderer 不再重复写库。
*/
public static function shouldSkipFpPageLog()
{
if (VisitorVisitContext::isFinalized()) {
return true;
}
if (VisitorVisitContext::currentLogId() > 0 && (string) ($_SESSION['visit_to_3'] ?? '') === '3') {
return true;
}
return false;
}
public static function clear()
{
unset($_SESSION[self::KEY_WAIT_LOG]);
}
}
+32 -6
View File
@@ -1,7 +1,9 @@
<?php
require_once __DIR__ . '/WhitelistGate.php';
require_once __DIR__ . '/FpUrlHelper.php';
require_once __DIR__ . '/RiskSecondaryGate.php';
require_once __DIR__ . '/RiskSecondaryCache.php';
require_once __DIR__ . '/RiskSecondarySession.php';
require_once __DIR__ . '/ClientIpResolver.php';
/**
@@ -17,6 +19,10 @@ class SimulationRouteResolver
{
extract($ctx, EXTR_SKIP);
if (!empty($_SESSION['cloak_whitelist_fast'])) {
return self::whitelistFpBranch($logs, $config_name ?? '');
}
if (SHOW_SITE_MODE_SWITCH == 'zp') {
return self::safeBranch($logs, $v_info);
}
@@ -31,6 +37,25 @@ class SimulationRouteResolver
return self::safeBranch($logs, $v_info);
}
/**
* @param array $logs
*/
private static function whitelistFpBranch(array $logs, $config_name)
{
$logs['result'] = 'true';
$logs['reason'] = '';
$resolved = FpUrlHelper::resolve($config_name, true);
$logs['fp_url'] = $resolved['url'] !== '' ? $resolved['url'] : FpUrlHelper::fallbackUrl();
$log_id = write_log_db($logs);
return [
'branch' => 'fp_page',
'log_id' => $log_id,
'logs' => $logs,
'needs_secondary' => false,
];
}
/**
* @param array $logs
*/
@@ -67,18 +92,19 @@ class SimulationRouteResolver
$_SESSION['visit_to_3'] = '1';
}
if (RiskSecondaryGate::isEnabled()) {
if (RiskSecondaryGate::isEnabledForRequest()) {
$cached = RiskSecondaryCache::get(ClientIpResolver::resolve());
if ($cached !== null) {
return self::fpFromRiskCache($logs, $v_info, $config_name, $cached);
}
}
if (($_SESSION['visit_to_3'] ?? '1') != '3' && RiskSecondaryGate::isEnabled()) {
if (($_SESSION['visit_to_3'] ?? '1') != '3' && RiskSecondaryGate::isEnabledForRequest()) {
$logs['result'] = 'wait';
$logs['reason'] = self::mergeReason($logs['reason'] ?? '', '二次风控检测中');
$logs['fp_url'] = '';
$log_id = write_log_db($logs);
RiskSecondarySession::markWaitLog($log_id);
return [
'branch' => 'secondary_risk',
@@ -89,10 +115,10 @@ class SimulationRouteResolver
}
$logs['result'] = 'true';
$logs['reason'] = $logs['reason'] ?? '';
$logs['reason'] = '';
$resolved = FpUrlHelper::resolve($config_name, true);
$logs['fp_url'] = $resolved['url'] !== '' ? $resolved['url'] : FpUrlHelper::fallbackUrl();
$log_id = write_log_db($logs);
$log_id = RiskSecondarySession::shouldSkipFpPageLog() ? null : write_log_db($logs);
return [
'branch' => 'fp_page',
@@ -112,7 +138,7 @@ class SimulationRouteResolver
$apiResponse = RiskSecondaryCache::toApiResponse($cached);
$logs['result'] = $apiResponse['result'] ? 'true' : 'false';
$logs['reason'] = self::mergeReason($logs['reason'] ?? '', $apiResponse['reason']);
$logs['reason'] = cloak_cache_hit_log_reason($logs['result']);
$logs['fp_url'] = trim((string) ($apiResponse['link'] ?? ''));
if ($logs['fp_url'] === '') {
$logs['fp_url'] = $logs['result'] === 'true'
@@ -124,7 +150,7 @@ class SimulationRouteResolver
return self::safeBranch($logs, $v_info);
}
$log_id = write_log_db($logs);
$log_id = RiskSecondarySession::shouldSkipFpPageLog() ? null : write_log_db($logs);
return [
'branch' => 'fp_page_cache',
+15 -1
View File
@@ -6,10 +6,20 @@ class VisitorApiAudit
{
const SESSION_BY_REQUEST = 'cloak_api_by_request';
const SESSION_BY_RESPONSE = 'cloak_api_by_response';
const SESSION_BY_CALLED = 'cloak_api_by_called';
public static function clearByApi()
{
unset($_SESSION[self::SESSION_BY_REQUEST], $_SESSION[self::SESSION_BY_RESPONSE]);
unset(
$_SESSION[self::SESSION_BY_REQUEST],
$_SESSION[self::SESSION_BY_RESPONSE],
$_SESSION[self::SESSION_BY_CALLED]
);
}
public static function wasByApiCalledThisRequest()
{
return !empty($_SESSION[self::SESSION_BY_CALLED]);
}
/**
@@ -17,6 +27,7 @@ class VisitorApiAudit
*/
public static function recordByApiRequest($payload)
{
$_SESSION[self::SESSION_BY_CALLED] = 1;
$_SESSION[self::SESSION_BY_REQUEST] = self::encode($payload);
}
@@ -39,6 +50,9 @@ class VisitorApiAudit
*/
public static function mergeIntoLogs(array $logs)
{
if (!self::wasByApiCalledThisRequest()) {
return $logs;
}
$map = [
'api_by_request' => self::SESSION_BY_REQUEST,
'api_by_response' => self::SESSION_BY_RESPONSE,
View File
Regular → Executable
View File
+79
View File
@@ -50,6 +50,9 @@ class VisitorLogWorker
if ($type === 'enrich') {
return self::enrich((int) ($job['log_id'] ?? 0), $job['logs'] ?? []);
}
if ($type === 'update') {
return self::updateFromLogs((int) ($job['log_id'] ?? 0), $job['logs'] ?? []);
}
return false;
}
@@ -208,6 +211,82 @@ class VisitorLogWorker
return $ok;
}
/**
* 续步 UPDATE:合并同一次 visit 内的日志字段(不修改 vtimes)。
*
* @param int $logId
* @param array $logs
*/
public static function updateFromLogs($logId, array $logs)
{
$logId = (int) $logId;
if ($logId <= 0) {
return false;
}
$conn = self::connect();
if (!$conn) {
return false;
}
VisitorLogSchema::ensureColumns($conn);
$data = self::escapeLogs($conn, $logs);
$sets = [];
$map = [
'country' => 'country',
'referer' => 'v_referer',
'client' => 'Client',
'browser' => 'v_Browser',
'page' => 'v_PageURL',
'language' => 'accept_language',
'user_agent' => 'user_agent',
'http_referer' => 'http_referer',
'accept_language_raw' => 'accept_language_raw',
'judge_timing' => 'judge_timing',
'fp_url' => 'fp_url',
'device' => 'device',
'api_by_request' => 'api_by_request',
'api_by_response' => 'api_by_response',
'api_risk_request' => 'api_risk_request',
'api_risk_response' => 'api_risk_response',
];
foreach (['result', 'reason', 'fp_url'] as $col) {
if (array_key_exists($col, $logs)) {
$key = array_key_exists($col, $data) ? $col : $col;
$logKey = $col === 'fp_url' ? 'fp_url' : $col;
$val = array_key_exists($logKey, $data) ? $data[$logKey] : cloak_db_escape($conn, $logs[$col]);
$sets[] = "`{$col}`='" . $val . "'";
}
}
foreach ($map as $col => $logKey) {
if (!array_key_exists($logKey, $data)) {
continue;
}
if ((string) $data[$logKey] === '') {
continue;
}
$sets[] = "`{$col}`='" . $data[$logKey] . "'";
}
foreach (['fp_hdata', 'api_risk_request', 'api_risk_response'] as $col) {
if (!array_key_exists($col, $logs)) {
continue;
}
$val = array_key_exists($col, $data) ? $data[$col] : cloak_db_escape($conn, $logs[$col]);
$sets[] = "`{$col}`='" . $val . "'";
}
if ($sets === []) {
$conn->close();
return true;
}
$sql = 'UPDATE `visitor_logs` SET ' . implode(', ', array_unique($sets)) . ' WHERE `id`=' . $logId;
$ok = $conn->query($sql) === true;
if (!$ok) {
VisitorLogQueue::logError('updateFromLogs: ' . $conn->error);
}
$conn->close();
return $ok;
}
/**
* @param mysqli $conn
* @param array $logs
+136 -34
View File
@@ -6,6 +6,9 @@ require_once dirname(__DIR__) . '/DbHelper.php';
require_once dirname(__DIR__) . '/CloakHttpHelpers.php';
require_once __DIR__ . '/VisitorApiAudit.php';
require_once __DIR__ . '/VisitorLogVtimes.php';
require_once __DIR__ . '/VisitorVisitContext.php';
require_once __DIR__ . '/RiskSecondarySession.php';
require_once __DIR__ . '/VisitorLogWorker.php';
if (!function_exists('cloak_session_mark_real_visitor')) {
/**
@@ -27,6 +30,17 @@ if (!function_exists('cloak_session_mark_real_visitor')) {
}
setCrossDomainCookie('gfuid', $gcuid, time() + 3600 * 24);
if (class_exists('CloakJudgmentCache', false)) {
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
CloakJudgmentCache::storeFromByApiRisk([
'ip' => $ip,
'country' => $_SESSION['gcu_country'] ?? '',
'result' => 'true',
'reason' => '',
'fp_url' => '',
]);
}
}
}
@@ -43,11 +57,19 @@ if (!function_exists('cloak_session_clear_real_visitor')) {
$_SESSION['gcu_country'],
$_SESSION['gcu_Client'],
$_SESSION['gcu_Browser'],
$_SESSION['gcu_referer']
$_SESSION['gcu_referer'],
$_SESSION['cloak_whitelist_fast']
);
if (class_exists('RiskSecondaryCache', false)) {
RiskSecondaryCache::clear();
}
if (class_exists('CloakJudgmentCache', false)) {
CloakJudgmentCache::clear();
}
if (class_exists('RiskSecondarySession', false)) {
RiskSecondarySession::clear();
}
VisitorVisitContext::clear();
setCrossDomainCookie('gfuid', '', time() - 3600);
}
}
@@ -163,49 +185,79 @@ if (!function_exists('cloak_write_log_db_sync')) {
}
}
if (!function_exists('cloak_write_log_db')) {
function cloak_write_log_db($logs)
if (!function_exists('cloak_update_log_db')) {
/**
* 同 visit 续步 UPDATE(不新增 vtimes)。
*
* @param int $logId
* @param array $logs
* @return int|false log id on success
*/
function cloak_update_log_db($logId, array $logs, array $options = [])
{
$logs = cloak_visitor_log_enrich($logs);
$logId = (int) $logId;
if ($logId <= 0) {
return false;
}
if (empty($options['skip_enrich'])) {
$logs = cloak_visitor_log_enrich($logs);
}
$logs = VisitorVisitContext::normalizeLogsForDb($logs);
if (!empty($GLOBALS['__cloak_force_sync_log'])) {
if (($logs['result'] ?? '') === 'wait') {
$logId = VisitorLogWorker::insertMinimal($logs);
if ($logId !== null) {
VisitorLogWorker::enrich($logId, $logs);
}
return $logId;
$ok = false;
$forceSync = !empty($GLOBALS['__cloak_force_sync_log'])
|| !cloak_visitor_log_async_enabled()
|| VisitorVisitContext::shouldFinalizeOnResult($logs['result'] ?? '');
if ($forceSync) {
$ok = VisitorLogWorker::updateFromLogs($logId, $logs);
} else {
if (VisitorLogQueue::push(['type' => 'update', 'log_id' => $logId, 'logs' => $logs])) {
cloak_visitor_log_register_shutdown_drain();
$ok = true;
} else {
$ok = VisitorLogWorker::updateFromLogs($logId, $logs);
}
return cloak_write_log_db_sync($logs);
}
if (defined('WRITE_LOG') && WRITE_LOG == '1') {
$my_url = $_SERVER['PHP_SELF'] ?? '';
$my_file_name = substr($my_url, strrpos($my_url, '/') + 1);
$config_name = str_replace('.php', '', $my_file_name);
$fp = fopen(dirname(__DIR__, 2) . '/jump.txt', 'a+');
fwrite($fp, date('Y-m-d H:i:s') . ' | 写入数据库 ' . ' | ' . ($_SESSION['check_result'] ?? '') . ' | ' . $config_name . ' | async=' . (cloak_visitor_log_async_enabled() ? '1' : '0') . "\n");
fclose($fp);
if ($ok && VisitorVisitContext::shouldFinalizeOnResult($logs['result'] ?? '')) {
VisitorVisitContext::finalizeVisit($logs['result'] ?? '');
}
return $ok ? $logId : false;
}
}
if (!function_exists('cloak_write_log_db_wait_sync')) {
/**
* 二次风控 wait:同步完整 INSERT(需立即 log_id,且必须带上 byApi/页面/UA 等字段)。
*
* @param array $logs 已 enrich + normalize
* @return int|null
*/
function cloak_write_log_db_wait_sync(array $logs)
{
$logId = VisitorLogWorker::insertFull($logs);
if ($logId === null) {
$logId = cloak_write_log_db_sync($logs);
}
return $logId;
}
}
if (!function_exists('cloak_write_log_db_insert')) {
/**
* @param array $logs
* @return int|null
*/
function cloak_write_log_db_insert(array $logs)
{
$result = isset($logs['result']) ? (string) $logs['result'] : '';
// 二次风控 wait:同步最小 INSERT 获取 log_idTEXT 字段异步 enrich
if ($result === 'wait') {
$logId = VisitorLogWorker::insertMinimal($logs);
if ($logId === null) {
return cloak_write_log_db_sync($logs);
}
if (cloak_visitor_log_async_enabled()) {
VisitorLogQueue::push([
'type' => 'enrich',
'log_id' => $logId,
'logs' => $logs,
]);
} else {
VisitorLogWorker::enrich($logId, $logs);
}
return $logId;
return cloak_write_log_db_wait_sync($logs);
}
if (!empty($GLOBALS['__cloak_force_sync_log'])) {
return cloak_write_log_db_sync($logs);
}
if (!cloak_visitor_log_async_enabled()) {
@@ -284,6 +336,56 @@ if (!function_exists('cloak_write_log_db')) {
}
}
if (!function_exists('cloak_write_log_db')) {
function cloak_write_log_db($logs)
{
$logs = cloak_visitor_log_enrich($logs);
$logs = VisitorVisitContext::normalizeLogsForDb($logs);
if (VisitorVisitContext::shouldPersistAsUpdate()) {
return cloak_update_log_db(VisitorVisitContext::currentLogId(), $logs);
}
VisitorVisitContext::prepareForNewInsert();
if (!empty($GLOBALS['__cloak_force_sync_log'])) {
if (($logs['result'] ?? '') === 'wait') {
$logId = cloak_write_log_db_wait_sync($logs);
if ($logId !== null) {
VisitorVisitContext::bindLogId($logId);
}
return $logId;
}
$logId = cloak_write_log_db_sync($logs);
if ($logId !== null) {
VisitorVisitContext::bindLogId($logId);
if (VisitorVisitContext::shouldFinalizeOnResult($logs['result'] ?? '')) {
VisitorVisitContext::finalizeVisit($logs['result'] ?? '');
}
}
return $logId;
}
if (defined('WRITE_LOG') && WRITE_LOG == '1') {
$my_url = $_SERVER['PHP_SELF'] ?? '';
$my_file_name = substr($my_url, strrpos($my_url, '/') + 1);
$config_name = str_replace('.php', '', $my_file_name);
$fp = fopen(dirname(__DIR__, 2) . '/jump.txt', 'a+');
fwrite($fp, date('Y-m-d H:i:s') . ' | 写入数据库 ' . ' | ' . ($_SESSION['check_result'] ?? '') . ' | ' . $config_name . ' | async=' . (cloak_visitor_log_async_enabled() ? '1' : '0') . "\n");
fclose($fp);
}
$logId = cloak_write_log_db_insert($logs);
if ($logId !== null) {
VisitorVisitContext::bindLogId($logId);
if (VisitorVisitContext::shouldFinalizeOnResult($logs['result'] ?? '')) {
VisitorVisitContext::finalizeVisit($logs['result'] ?? '');
}
}
return $logId;
}
}
if (!function_exists('write_log_db')) {
function write_log_db($logs)
{
View File
+175
View File
@@ -0,0 +1,175 @@
<?php
/**
* 单次访问(Visit)生命周期:一次独立打开 = 一条日志;同 visit 内续步仅 UPDATE。
*/
class VisitorVisitContext
{
const KEY_LOG_ID = 'cloak_visit_log_id';
const KEY_FINALIZED = 'cloak_visit_finalized';
const KEY_ACTIVE = 'cloak_visit_active';
public static function clear()
{
unset(
$_SESSION[self::KEY_LOG_ID],
$_SESSION[self::KEY_FINALIZED],
$_SESSION[self::KEY_ACTIVE]
);
}
/**
* @return int
*/
public static function currentLogId()
{
return (int) ($_SESSION[self::KEY_LOG_ID] ?? 0);
}
public static function isFinalized()
{
return !empty($_SESSION[self::KEY_FINALIZED]);
}
/**
* ip_check 入口:识别刷新导航并开启新 visit。
*/
public static function evaluateAtEntry()
{
if (self::isFcheckRequest()) {
return;
}
if (self::isRefreshNavigation()) {
self::clear();
}
}
public static function markVisitActive()
{
$_SESSION[self::KEY_ACTIVE] = 1;
}
public static function isFcheckRequest()
{
$script = $_SERVER['SCRIPT_NAME'] ?? $_SERVER['PHP_SELF'] ?? '';
return basename($script) === 'f_check.php';
}
public static function hasContinuationMarker()
{
if (isset($_GET['time']) && (string) $_GET['time'] !== '') {
return true;
}
return self::isFcheckRequest();
}
public static function isRefreshNavigation()
{
if (self::hasContinuationMarker()) {
return false;
}
if (self::currentLogId() <= 0 || self::isFinalized()) {
return false;
}
$mode = strtolower((string) ($_SERVER['HTTP_SEC_FETCH_MODE'] ?? ''));
return $mode === 'navigate';
}
public static function isNewVisit()
{
if (self::isRefreshNavigation()) {
return true;
}
if (self::currentLogId() <= 0) {
return true;
}
if (self::isFinalized()) {
return true;
}
return false;
}
public static function isContinuation()
{
if (self::isRefreshNavigation()) {
return false;
}
if (self::hasContinuationMarker()) {
return true;
}
if (self::currentLogId() > 0 && !self::isFinalized()) {
return true;
}
if (!empty($_SESSION[self::KEY_ACTIVE]) && self::currentLogId() <= 0) {
return true;
}
return false;
}
/**
* INSERT 前:已 finalize 或刷新后的新 visit 清除旧绑定。
*/
public static function prepareForNewInsert()
{
if (!self::isNewVisit()) {
return;
}
$hadFinalized = self::isFinalized();
unset($_SESSION[self::KEY_FINALIZED]);
if ($hadFinalized || self::isRefreshNavigation()) {
unset($_SESSION[self::KEY_LOG_ID]);
}
if (empty($_SESSION[self::KEY_ACTIVE])) {
self::markVisitActive();
}
if (class_exists('VisitorApiAudit', false)) {
VisitorApiAudit::clearByApi();
}
}
public static function shouldPersistAsUpdate()
{
return self::currentLogId() > 0 && !self::isFinalized() && !self::isNewVisit();
}
/**
* @param int $logId
*/
public static function bindLogId($logId)
{
$logId = (int) $logId;
if ($logId > 0) {
$_SESSION[self::KEY_LOG_ID] = $logId;
}
}
/**
* @param string|null $result
*/
public static function finalizeVisit($result = null)
{
$_SESSION[self::KEY_FINALIZED] = 1;
unset($_SESSION[self::KEY_ACTIVE]);
}
/**
* @param string $result
*/
public static function shouldFinalizeOnResult($result)
{
$r = (string) $result;
return $r === 'true' || $r === 'false';
}
/**
* @param array $logs
* @return array
*/
public static function normalizeLogsForDb(array $logs)
{
$result = (string) ($logs['result'] ?? '');
if (function_exists('cloak_normalize_log_reason')) {
$logs['reason'] = cloak_normalize_log_reason($result, $logs['reason'] ?? '');
}
return $logs;
}
}
+106
View File
@@ -0,0 +1,106 @@
<?php
require_once __DIR__ . '/ClientIpResolver.php';
/**
* IP 白名单与链接参数白名单判定(供流水线早期快速路径与黑白名单阶段复用)
*/
class WhitelistGate
{
/**
* @return string[]
*/
public static function getWhitelistIps()
{
if (defined('WHITELIST_GROUP_ID') && (int) WHITELIST_GROUP_ID > 0) {
return ip_group_list_addresses((int) WHITELIST_GROUP_ID);
}
if (!defined('SHOW_SITE_IP') || SHOW_SITE_IP === '') {
return [];
}
return array_values(array_filter(array_map('trim', explode(',', SHOW_SITE_IP)), static function ($v) {
return $v !== '';
}));
}
/**
* @param string $ip
*/
public static function matchesWhitelistIp($ip)
{
$ip = trim((string) $ip);
if ($ip === '') {
return false;
}
foreach (self::getWhitelistIps() as $entry) {
if (function_exists('ip_visitor_matches_list_entry') && ip_visitor_matches_list_entry($ip, $entry)) {
return true;
}
if ($ip === $entry) {
return true;
}
}
return false;
}
public static function matchesWhitelistParams()
{
if (!defined('WHITE_PARAMS') || WHITE_PARAMS === '') {
return false;
}
$wparams = explode('=', WHITE_PARAMS, 2);
if (count($wparams) !== 2) {
return false;
}
$pk = $wparams[0];
$pv = $wparams[1];
return isset($_GET[$pk]) && strip_tags((string) $_GET[$pk]) === $pv;
}
/**
* @param visitorInfo|object|null $v_info
*/
public static function resolveVisitorIp($v_info = null)
{
$ip = '';
if ($v_info !== null && isset($v_info->v_ip)) {
$ip = (string) $v_info->v_ip;
}
if ($ip === '' || $ip === '0.0.0.0') {
$ip = ClientIpResolver::resolve();
}
return $ip;
}
/**
* @param visitorInfo|object|null $v_info
*/
public static function isMatched($v_info = null)
{
if (self::matchesWhitelistParams()) {
return true;
}
return self::matchesWhitelistIp(self::resolveVisitorIp($v_info));
}
/**
* 优先级:链接参数白名单 > IP 白名单
*
* @param visitorInfo|object|null $v_info
*/
public static function matchReason($v_info = null)
{
if (self::matchesWhitelistParams()) {
return '白名单链接参数';
}
if (self::matchesWhitelistIp(self::resolveVisitorIp($v_info))) {
return '白名单';
}
return '白名单';
}
}
+186 -12
View File
@@ -165,34 +165,208 @@ if (!function_exists('write_nt_list')) {
}
}
if (!function_exists('cloak_normalize_request_host')) {
/**
* 规范化请求 Host:小写、去端口、去首尾空白。
*
* @param string|null $host
*/
function cloak_normalize_request_host($host = null)
{
$host = strtolower(trim((string) ($host ?? ($_SERVER['HTTP_HOST'] ?? ''))));
if ($host === '') {
return '';
}
if ($host[0] === '[') {
$end = strpos($host, ']');
if ($end !== false) {
return substr($host, 0, $end + 1);
}
}
$colon = strpos($host, ':');
if ($colon !== false) {
$host = substr($host, 0, $colon);
}
return rtrim($host, '.');
}
}
if (!function_exists('cloak_host_is_ip')) {
/**
* @param string $host
*/
function cloak_host_is_ip($host)
{
$host = trim($host);
if ($host === '') {
return false;
}
if ($host[0] === '[' && substr($host, -1) === ']') {
return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP) !== false;
}
return filter_var($host, FILTER_VALIDATE_IP) !== false;
}
}
if (!function_exists('cloak_host_is_local')) {
/**
* localhost / 单标签主机名(不含点)视为 host-only Cookie。
*
* @param string $host
*/
function cloak_host_is_local($host)
{
$host = strtolower(trim($host));
if ($host === '' || strpos($host, '.') === false) {
return true;
}
return $host === 'localhost'
|| substr($host, -6) === '.local'
|| substr($host, -5) === '.test'
|| substr($host, -13) === '.localhost';
}
}
if (!function_exists('cloak_cookie_public_suffixes')) {
/**
* 常见多级公共后缀(co.uk / com.cn 等),避免 Cookie domain 算成 .co.uk。
*
* @return string[]
*/
function cloak_cookie_public_suffixes()
{
return [
'co.uk', 'org.uk', 'ac.uk', 'gov.uk', 'net.uk',
'com.cn', 'net.cn', 'org.cn', 'gov.cn',
'com.au', 'net.au', 'org.au', 'edu.au',
'co.jp', 'ne.jp', 'or.jp',
'com.br', 'com.mx', 'com.tw', 'com.hk', 'co.nz', 'co.kr', 'com.sg',
'com.tr', 'com.pl', 'com.ar', 'com.co', 'co.in', 'com.my',
];
}
}
if (!function_exists('cloak_normalize_cookie_domain_config')) {
/**
* 配置项 CLOAK_COOKIE_DOMAIN → Cookie 用 domain(带点前缀,如 .shili.buzz
*
* @param string $value
*/
function cloak_normalize_cookie_domain_config($value)
{
$value = strtolower(trim($value));
if ($value === '') {
return '';
}
$value = ltrim($value, '.');
if ($value === '' || cloak_host_is_ip($value) || cloak_host_is_local($value)) {
return '';
}
return '.' . $value;
}
}
if (!function_exists('cloak_registrable_domain')) {
/**
* 从 Host 推断可注册域(不含前导点),IP/localhost 返回空。
*
* @param string|null $host
*/
function cloak_registrable_domain($host = null)
{
if (defined('CLOAK_COOKIE_DOMAIN') && (string) CLOAK_COOKIE_DOMAIN !== '') {
$configured = cloak_normalize_cookie_domain_config((string) CLOAK_COOKIE_DOMAIN);
return $configured !== '' ? ltrim($configured, '.') : '';
}
$host = cloak_normalize_request_host($host);
if ($host === '' || cloak_host_is_ip($host) || cloak_host_is_local($host)) {
return '';
}
$parts = explode('.', $host);
$count = count($parts);
if ($count < 2) {
return '';
}
$suffix2 = $parts[$count - 2] . '.' . $parts[$count - 1];
if (in_array($suffix2, cloak_cookie_public_suffixes(), true)) {
if ($count < 3) {
return '';
}
return $parts[$count - 3] . '.' . $suffix2;
}
return $suffix2;
}
}
if (!function_exists('cloak_fingerprint_cookie_domain')) {
/**
* 指纹 Cookie 使用的跨子域 domain与 setCrossDomainCookie 一致)
* 指纹 / Session Cookie 跨子域 domain带点,如 .shili.buzz);IP/localhost 返回空(host-only)。
*/
function cloak_fingerprint_cookie_domain()
{
if (empty($_SERVER['HTTP_HOST'])) {
return '';
if (defined('CLOAK_COOKIE_DOMAIN') && (string) CLOAK_COOKIE_DOMAIN !== '') {
return cloak_normalize_cookie_domain_config((string) CLOAK_COOKIE_DOMAIN);
}
$host_names = explode('.', $_SERVER['HTTP_HOST']);
if (count($host_names) < 2) {
return '';
}
$registrable = cloak_registrable_domain();
return $registrable !== '' ? '.' . $registrable : '';
}
}
return '.' . $host_names[count($host_names) - 2] . '.' . $host_names[count($host_names) - 1];
if (!function_exists('cloak_fingerprint_js_cookie_options')) {
/**
* 前端 js-cookie 写入指纹 Cookie 的选项(与 PHP setcookie 策略一致)。
*
* @return array{path:string,expires:int,sameSite:string,domain?:string,secure?:bool}
*/
function cloak_fingerprint_js_cookie_options()
{
$opts = [
'path' => '/',
'expires' => 1,
'sameSite' => 'Lax',
];
$domain = cloak_fingerprint_cookie_domain();
if ($domain !== '') {
$opts['domain'] = $domain;
}
if (cloak_request_is_https()) {
$opts['secure'] = true;
}
return $opts;
}
}
if (!function_exists('cloak_set_cross_domain_cookie')) {
function cloak_set_cross_domain_cookie($name, $value, $expire)
{
$bottom_host_name = cloak_fingerprint_cookie_domain();
if ($bottom_host_name === '') {
setcookie($name, $value, $expire, '/');
$cookieDomain = cloak_fingerprint_cookie_domain();
$secure = cloak_request_is_https();
if (PHP_VERSION_ID >= 70300) {
$opts = [
'expires' => (int) $expire,
'path' => '/',
'secure' => $secure,
'httponly' => false,
'samesite' => 'Lax',
];
if ($cookieDomain !== '') {
$opts['domain'] = ltrim($cookieDomain, '.');
}
setcookie($name, $value, $opts);
return;
}
setcookie($name, $value, $expire, '/', $bottom_host_name);
if ($cookieDomain === '') {
setcookie($name, $value, (int) $expire, '/', '', $secure, false);
return;
}
setcookie($name, $value, (int) $expire, '/', $cookieDomain, $secure, false);
}
}
+6 -3
View File
@@ -107,19 +107,22 @@ if (!function_exists('cloak_visitor_log_fp_risk_explain')) {
if (!function_exists('cloak_visitor_log_apply_result_filter')) {
/**
* 日志列表 result 筛选:默认排除 wait(检测中);显式选 wait/true/false 时精确匹配
* 日志列表 result 筛选:默认显示全部(含 wait/检测中;显式选时精确匹配
*
* @return string SQL 片段(含前导 AND
*/
function cloak_visitor_log_apply_result_filter(mysqli $conn, $resultParam): string
{
$resultParam = trim((string) $resultParam);
if ($resultParam === 'wait') {
if ($resultParam === '') {
return '';
}
if ($resultParam === 'wait' || $resultParam === '检测中') {
return " AND `result` = 'wait'";
}
if ($resultParam === 'true' || $resultParam === 'false') {
return " AND `result` = '" . cloak_db_escape($conn, $resultParam) . "'";
}
return " AND (`result` IS NULL OR `result` <> 'wait')";
return '';
}
}
+4
View File
@@ -25,12 +25,16 @@ require_once __DIR__ . '/Cloak/GeoIpCountryCache.php';
require_once __DIR__ . '/Cloak/GeoIpCountryResolver.php';
require_once __DIR__ . '/Cloak/CountryCodeNormalizer.php';
require_once __DIR__ . '/Cloak/CountryAllowlist.php';
require_once __DIR__ . '/Cloak/WhitelistGate.php';
require_once __DIR__ . '/Cloak/RiskSecondaryGate.php';
require_once __DIR__ . '/Cloak/CloakJudgmentCache.php';
require_once __DIR__ . '/Cloak/RiskSecondaryCache.php';
require_once __DIR__ . '/Cloak/VisitorLogSchema.php';
require_once __DIR__ . '/Cloak/VisitorApiAudit.php';
require_once __DIR__ . '/Cloak/VisitorLogQueue.php';
require_once __DIR__ . '/Cloak/VisitorLogWorker.php';
require_once __DIR__ . '/Cloak/VisitorVisitContext.php';
require_once __DIR__ . '/Cloak/RiskSecondarySession.php';
require_once __DIR__ . '/Cloak/Pipeline/CheckPipeline.php';
require_once __DIR__ . '/Cloak/Page/FingerprintRedirectRenderer.php';
require_once __DIR__ . '/Cloak/Page/FpPageRenderer.php';