Files
CLOAK/__code__/risk_analyzer.php
2026-06-14 18:30:06 +08:00

1115 lines
60 KiB
PHP
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==*==**/
// 风险评估类
class RiskAnalyzer {
public $countries;
public $countriesByContinent;
public $langLabels;
public $countryLangMap;
public $commonResolutions;
public $timezones;
public $countryTimezoneMap;
public $unqiePixels;
private $riskLevel = 0;
private $riskFactors = [];
private $fingerprint;
private $fingerprintData;
public function __construct($data) {
$this->fingerprintData = $data;
$this->hashFingerprint();
}
private function getScreenWidth() {
$screen = isset($this->fingerprintData['screen']) ? $this->fingerprintData['screen'] : null;
if (!is_array($screen)) {
return 0;
}
if (isset($screen['width'])) {
return (int) $screen['width'];
}
return (int) (isset($screen[0]) ? $screen[0] : 0);
}
private function hasTouchscreen() {
$touch = isset($this->fingerprintData['touchscreen']) ? $this->fingerprintData['touchscreen'] : array(0, false, false);
if (!is_array($touch)) {
return false;
}
return ($touch[0] > 0 || !empty($touch[1]) || !empty($touch[2]));
}
private function uaIndicatesTablet($ua) {
return (bool) preg_match('/iPad|Tablet|SM-T|Kindle|Silk\/|PlayBook|Tab(?:let)?\b/i', $ua);
}
private function uaIndicatesMobile($ua) {
return (bool) preg_match(
'/iPhone|iPod|Android.*Mobile|Mobile\/|IEMobile|webOS|BlackBerry|Opera Mini|Mobile Safari/i',
$ua
);
}
private function platformIndicatesMobile() {
$platform = isset($this->fingerprintData['platform']) ? (string) $this->fingerprintData['platform'] : '';
return $platform !== '' && (bool) preg_match('/iPhone|iPod|Android/i', $platform);
}
private function platformIndicatesTablet() {
$platform = isset($this->fingerprintData['platform']) ? (string) $this->fingerprintData['platform'] : '';
return $platform !== '' && (bool) preg_match('/iPad/i', $platform);
}
/**
* 推断设备类型(browser=null 时不能默认 desktop
*/
private function getDeviceType() {
$ua = isset($this->fingerprintData['userAgent']) ? (string) $this->fingerprintData['userAgent'] : '';
if ($this->uaIndicatesTablet($ua) || $this->platformIndicatesTablet()) {
return 'tablet';
}
if ($this->uaIndicatesMobile($ua) || $this->platformIndicatesMobile()) {
return 'mobile';
}
$parsedType = '';
if (!empty($this->fingerprintData['browser']['device']['type'])) {
$parsedType = strtolower((string) $this->fingerprintData['browser']['device']['type']);
}
if (in_array($parsedType, array('mobile', 'tablet', 'wearable', 'smarttv', 'console', 'embedded', 'xr'), true)) {
return $parsedType;
}
if ($this->hasTouchscreen()) {
$screenWidth = $this->getScreenWidth();
if ($screenWidth > 0 && $screenWidth < 1024) {
return 'mobile';
}
}
if ($parsedType !== '') {
return $parsedType;
}
return 'desktop';
}
private function isMobileDeviceType($deviceType) {
return in_array($deviceType, array('mobile', 'tablet'), true);
}
/**
* 移动端 screen 与 availScreen 相同是正常现象,不能作为桌面风险信号
*/
private function shouldApplyDesktopScreenHeuristics() {
if ($this->isMobileDeviceType($this->getDeviceType())) {
return false;
}
if ($this->hasTouchscreen()) {
return false;
}
$ua = isset($this->fingerprintData['userAgent']) ? (string) $this->fingerprintData['userAgent'] : '';
if ($this->uaIndicatesMobile($ua) || $this->uaIndicatesTablet($ua)) {
return false;
}
if ($this->platformIndicatesMobile() || $this->platformIndicatesTablet()) {
return false;
}
return true;
}
private function getOsName() {
$platform = isset($this->fingerprintData['platform']) ? (string) $this->fingerprintData['platform'] : '';
if (preg_match('/iPad|iPhone|iPod/i', $platform)) {
return 'iOS';
}
if (!empty($this->fingerprintData['browser']['os']['name'])) {
$os = (string) $this->fingerprintData['browser']['os']['name'];
if (preg_match('/macOS/i', $os) && ($this->platformIndicatesMobile() || $this->platformIndicatesTablet())) {
return 'iOS';
}
return $os;
}
$ua = isset($this->fingerprintData['userAgent']) ? (string) $this->fingerprintData['userAgent'] : '';
if (preg_match('/Windows/i', $ua)) {
return 'Windows';
}
if (preg_match('/Android/i', $ua)) {
return 'Android';
}
if (preg_match('/iPhone|iPad|iOS/i', $ua)) {
return 'iOS';
}
if (preg_match('/Mac OS X|Macintosh/i', $ua)) {
if ($this->platformIndicatesMobile() || $this->platformIndicatesTablet() || $this->hasTouchscreen()) {
return 'iOS';
}
return 'macOS';
}
return null;
}
// 执行完整风险评估
public function analyze() {
$this->checkWebDriver();
$this->checkUserAgent();
$this->checkWebGLRenderer();
$this->checkHardwareConcurrency();
$this->checkDeviceMemory();
$this->checkTouchscreenConsistency();
$this->checkScreenResolution();
$this->checkBrowserFeatures();
$this->checkTimezoneAnomalies();
$this->checkLanguages();
$this->checkHostname();
$this->checkFonts();
// 限制风险等级在0-100之间
$this->riskLevel = min(100, max(0, $this->riskLevel));
return [
'fingerprint' => $this->fingerprint,
'risk_level' => $this->riskLevel,
'risk_factors' => $this->riskFactors,
'risk_category' => $this->getRiskCategory(),
'risk_description' => $this->getRiskDescription()
];
}
public function hashFingerprint() {
if (empty($this->fingerprintData['canvas']) || !is_array($this->fingerprintData['canvas'])) {
return;
}
if ($this->fingerprintData['canvas'][0] == 'canvas winding:yes') {
$this->fingerprint = md5($this->fingerprintData['canvas'][0] . $this->fingerprintData['canvas'][1]);
} else {
$this->fingerprint = NULL;
$this->riskLevel += 50;
$this->riskFactors[] = "检测CANVAS无效";
}
}
// 检查WebDriver状态(强风险指标)
private function checkWebDriver() {
if (!empty($this->fingerprintData['webdriver'])) {
$this->riskLevel += 50;
$this->riskFactors[] = "检测到WebDriver,通常与自动化工具相关";
}
}
// 检查User Agent中的自动化工具特征
private function checkUserAgent() {
if (empty($this->fingerprintData['userAgent'])) return;
$userAgent = strtolower($this->fingerprintData['userAgent']);
$botIndicators = [
'bot', 'crawl', 'spider', 'slurp', 'search', 'semrush',
'webdriver', 'selenium', 'phantom', 'headless',
'chromedriver', 'geckodriver', 'robot', 'scrap', 'screenshot', 'monitoring',
'AdsBot-Google','facebookexternalhit','Google-Adwords-Instant', 'emulator', 'simulator'
];
// 检测爬虫特征
foreach ($botIndicators as $indicator) {
if (stripos($userAgent, $indicator) !== false) {
$this->riskLevel += 30;
$this->riskFactors[] = "User Agent包含自动化工具特征: {$indicator}";
}
}
// 检查无头浏览器特征
if (stripos($userAgent, 'headlesschrome') !== false) {
$this->riskLevel += 40;
$this->riskFactors[] = "检测到无头Chrome浏览器,通常用于自动化";
}
// 检测用户代理异常
$browsers = ['Chrome', 'Firefox', 'Safari', 'Edge'];
$os = ['Windows', 'Mac', 'Android', 'iOS'];
$browserDetected = false;
$osDetected = false;
foreach ($browsers as $browser) {
if (stripos($this->fingerprintData['userAgent'], $browser) !== false) {
$browserDetected = true;
break;
}
}
foreach ($os as $osName) {
if (stripos($this->fingerprintData['userAgent'], $osName) !== false) {
$osDetected = true;
break;
}
}
if (!$browserDetected || !$osDetected) {
$this->riskLevel += 15;
$this->riskFactors[] = "异常用户代理: 无法识别浏览器或操作系统";
}
if (isset($this->fingerprintData['automation'])) {
if ($this->fingerprintData['automation']['phantom'] || $this->fingerprintData['automation']['selenium']) {
$this->riskLevel += 30;
$this->riskFactors[] = '检测到PhantomJS或Selenium相关组件。';
}
// UA声称是Chrome,但没有`window.chrome`对象,非常可疑
if (strpos($userAgent, 'Chrome') !== false && !$this->fingerprintData['automation']['isChrome']) {
$this->riskLevel += 20;
$this->riskFactors[] = 'UA声称是Chrome,但没有`window.chrome`对象,非常可疑';
}
}
$renderer = isset($this->fingerprintData['renderer']) ? $this->fingerprintData['renderer'] : '';
$os = $this->getOsName();
if ($renderer === '' || $os === null) {
return;
}
// 检查是否为Windows系统
if (preg_match('/Windows/i', $os)) {
// Windows 系统的 GPU 应该是 NVIDIA, AMD, Intel, 或者 Microsoft Basic Render Driver
// 绝对不应该是 Apple
if (stripos($renderer, 'apple') !== false) {
$this->riskLevel += 10;
$this->riskFactors[] = "CRITICAL: Windows User-Agent but an Apple GPU renderer was detected. Clear sign of spoofing.";
}
}
// 检查是否为macOS系统
elseif (preg_match('/macOS/i', $os)) {
// macOS 系统的 GPU 应该是 Apple, Intel, 或 AMD (在旧款Mac Pro上)
// 现代Mac上几乎不可能是 NVIDIA
if (strpos($renderer, 'nvidia') !== false) {
$this->riskLevel += 10;
$this->riskFactors[] = "CRITICAL: macOS User-Agent but an NVIDIA GPU renderer was detected. Highly inconsistent.";
}
// Mac的renderer不应包含Direct3D
if (stripos($renderer, 'direct3d') !== false) {
$this->riskLevel += 10;
$this->riskFactors[] = "CRITICAL: macOS User-Agent but renderer string contains 'Direct3D', which is Windows-specific.";
}
}
}
// 检查WebGL渲染器是否为虚拟环境特征
private function checkWebGLRenderer() {
if (empty($this->fingerprintData['renderer'])) return;
$vendor = $this->fingerprintData['vendor'];
$renderer = $this->fingerprintData['renderer'];
$virtualRenderers = [
'llvmpipe', 'software', 'virtual', 'qemu', 'mesa', 'swiftshader', 'VMware', 'VirtualBox', 'KVM', 'Xen', 'Hyper-V', 'AWS', 'Google Compute', 'Azure', 'Headless', 'OpenGL', 'Parallels'
];
foreach ($virtualRenderers as $virt) {
if (stripos($renderer, $virt) !== false) {
$this->riskLevel += 30;
$this->riskFactors[] = "检测到可能的虚拟环境图形渲染器: {$this->fingerprintData['renderer']}";
}
}
if (empty($this->fingerprintData['webgl']) || !is_array($this->fingerprintData['webgl'])) {
return;
}
$extensions = explode(';', $this->fingerprintData['webgl'][1]);
if (count($extensions) < 28) {
$this->riskLevel += 10;
$this->riskFactors[] = "WebGL扩展数量较少.";
}
$lineWidthRangeStr = str_replace("webgl aliased line width range:", "", $this->fingerprintData['webgl'][2]);
$lineWidthRange = $lineWidthRangeStr ? json_decode($lineWidthRangeStr, true) : [0,0];
if (is_array($lineWidthRange) && $lineWidthRange[0] == 1 && $lineWidthRange[1] == 1) {
$this->riskLevel += 2;
$this->riskFactors[] = "Aliased line width range is [1, 1]";
}
$MAX_TEXTURE_SIZE = str_replace("webgl max texture size:", "", $this->fingerprintData['webgl'][15]);
if (isset($this->fingerprintData['webgl'][15]) && $MAX_TEXTURE_SIZE < 8192) {
$this->riskLevel += 2;
$this->riskFactors[] = "SUSPICIOUS: MAX_TEXTURE_SIZE is low ($MAX_TEXTURE_SIZE).";
}
$MAX_FRAGMENT_UNIFORM_VECTORS = str_replace("webgl max fragment uniform vectors:", "", $this->fingerprintData['webgl'][12]);
if (isset($this->fingerprintData['webgl'][12]) && $MAX_FRAGMENT_UNIFORM_VECTORS < 1024) {
$this->riskLevel += 2;
$this->riskFactors[] = "SUSPICIOUS: MAX_FRAGMENT_UNIFORM_VECTORS is low ($MAX_FRAGMENT_UNIFORM_VECTORS).";
}
$MAX_VARYING_VECTORS = str_replace("webgl max varying vectors:", "", $this->fingerprintData['webgl'][16]);
if (isset($this->fingerprintData['webgl'][16]) && $MAX_VARYING_VECTORS < 16) {
$this->riskLevel += 2;
$this->riskFactors[] = "SUSPICIOUS: MAX_VARYING_VECTORS is low ($MAX_VARYING_VECTORS).";
}
$max_viewport_dims_str = str_replace("webgl max viewport dims:", "", $this->fingerprintData['webgl'][20]);
$MAX_VIEWPORT_DIMS = $max_viewport_dims_str ? json_decode($max_viewport_dims_str, true) : [0,0];
if (isset($this->fingerprintData['webgl'][20]) && is_array($MAX_VIEWPORT_DIMS) && $MAX_VIEWPORT_DIMS[0] < 8192) {
$this->riskLevel += 2;
$this->riskFactors[] = "SUSPICIOUS: MAX_VIEWPORT_DIMS is low.";
}
$MAX_ANISOTROPY = str_replace("webgl max anisotropy:", "", $this->fingerprintData['webgl'][9]);
if (isset($this->fingerprintData['webgl'][9]) && (int) $MAX_ANISOTROPY <= 2) {
$this->riskLevel += 5;
$this->riskFactors[] = "SUSPICIOUS: Anisotropic filtering is not supported.";
}
// 规则4:上下文配置检查
// 前端请求了抗锯齿,检查后端实际是否获得
$antialiasing = str_replace("webgl antialiasing:", "", $this->fingerprintData['webgl'][5]);
if (isset($this->fingerprintData['webgl'][5]) && $antialiasing != 'yes') {
$this->riskLevel += 10;
$this->riskFactors[] = "SUSPICIOUS: Antialiasing was requested but not enabled, common in software renderers.";
}
}
// 检查IP的Hostname
private function checkHostname() {
$ip = isset($this->fingerprintData['ip']) ? trim((string) $this->fingerprintData['ip']) : '';
if ($ip === '' || !filter_var($ip, FILTER_VALIDATE_IP)) {
return;
}
$hostname = @gethostbyaddr($ip);
if ($hostname && $hostname !== $ip) {
if (preg_match('/\.googlebot\.com$/i', $hostname) || preg_match('/\.google\.com$/i', $hostname)) {
$this->riskLevel += 30;
$this->riskFactors[] = "检测到是Google爬虫";
} elseif (preg_match('/\.facebook\.com$/i', $hostname)) {
$this->riskLevel += 30;
$this->riskFactors[] = "检测到是Facebook爬虫";
} elseif (preg_match('/\.tiktok\.com$/i', $hostname)) {
$this->riskLevel += 30;
$this->riskFactors[] = "检测到是Tiktok爬虫";
}
}
}
// 检查硬件并发数是否异常低
private function checkHardwareConcurrency() {
if (empty($this->fingerprintData['hardwareConcurrency'])) return;
$cores = $this->fingerprintData['hardwareConcurrency'];
if ($cores <= 2) {
$this->riskLevel += 15;
$this->riskFactors[] = "硬件并发数异常低: {$cores}核,可能是虚拟机环境";
}
}
// 检查设备内存是否异常低
private function checkDeviceMemory() {
if (empty($this->fingerprintData['deviceMemory'])) return;
if ($this->fingerprintData['deviceMemory'] < 2) {
$this->riskLevel += 10;
$this->riskFactors[] = "低设备内存: " . $this->fingerprintData['deviceMemory'] . "GB";
}
}
// 检查触摸屏与设备类型是否一致
private function checkTouchscreenConsistency() {
$deviceType = $this->getDeviceType();
$isMobile = $this->isMobileDeviceType($deviceType);
$hasTouchscreen = $this->hasTouchscreen();
// 移动设备没有触摸屏
if ($isMobile && !$hasTouchscreen) {
$this->riskLevel += 20;
$this->riskFactors[] = "移动设备报告没有触摸屏,可能是自动化环境";
}
// 桌面设备有触摸屏但分辨率异常低
if ($deviceType == 'desktop' && $hasTouchscreen) {
$screenWidth = $this->getScreenWidth();
if ($screenWidth > 0 && $screenWidth < 1024) {
$this->riskLevel += 5;
$this->riskFactors[] = "桌面设备有触摸屏但分辨率异常低,可能是模拟环境";
}
}
// 移动设备仿真器,没有加速和陀螺仪
if ($isMobile && !empty($this->fingerprintData['isEmulators'])) {
$this->riskLevel += 15;
$this->riskFactors[] = "设备可能是仿真器";
}
// 检查是否为移动端
if ($isMobile) {
$renderer = isset($this->fingerprintData['renderer']) ? $this->fingerprintData['renderer'] : '';
if ($renderer !== '' && preg_match('/(nvidia|geforce|radeon)/i', $renderer)) {
$this->riskLevel += 10;
$this->riskFactors[] = "CRITICAL: Mobile User-Agent but a desktop-class GPU (NVIDIA/AMD) was detected.";
}
$os = $this->getOsName();
if ($os !== null && !preg_match('/Android/i', $os) && !preg_match('/iOS/i', $os)) {
$this->riskLevel += 10;
$this->riskFactors[] = "移动端仅支持安卓与IOS";
}
}
}
// 检查屏幕分辨率是否在常见范围内
private function checkScreenResolution() {
if (empty($this->fingerprintData['screen']) || !is_array($this->fingerprintData['screen'])) return;
$screenSize = $this->fingerprintData['screen'];
$screenWidth = (int) (isset($screenSize[0]) ? $screenSize[0] : (isset($screenSize['width']) ? $screenSize['width'] : 0));
$screenHeight = (int) (isset($screenSize[1]) ? $screenSize[1] : (isset($screenSize['height']) ? $screenSize['height'] : 0));
$availScreenSize = isset($this->fingerprintData['availScreen']) ? $this->fingerprintData['availScreen'] : array(0, 0);
$availScreenWidth = (int) (isset($availScreenSize[0]) ? $availScreenSize[0] : (isset($availScreenSize['width']) ? $availScreenSize['width'] : 0));
$availScreenHeight = (int) (isset($availScreenSize[1]) ? $availScreenSize[1] : (isset($availScreenSize['height']) ? $availScreenSize['height'] : 0));
// 分辨率过小
if ($screenWidth < 300 || $screenHeight < 400) {
$this->riskLevel += 15;
$this->riskFactors[] = "分辨率异常[宽或高过小]";
}
// 仅真实桌面环境:手机/平板 screen 与 availScreen 相同是正常现象
if ($this->shouldApplyDesktopScreenHeuristics()) {
if ($screenWidth === $availScreenWidth && $screenHeight === $availScreenHeight) {
$this->riskLevel += 20;
$this->riskFactors[] = "分辨率异常[桌面设备可用分辨率完全相同]";
}
$widthDiff = $screenWidth - $availScreenWidth;
$heightDiff = $screenHeight - $availScreenHeight;
if ($widthDiff < 10 && $heightDiff < 10 && $screenWidth > 1000) {
$this->riskLevel += 10;
$this->riskFactors[] = "分辨率异常[3]";
}
}
// 分辨率小于0
if ($screenWidth <= 0 || $screenHeight <= 0 ||
$availScreenWidth <= 0 || $availScreenHeight <= 0) {
$this->riskLevel += 40;
$this->riskFactors[] = "检测到无效或零分辨率";
}
// 常见分辨率与已知设备不匹配(unqiePixels 未配置时跳过)
if (is_array($this->unqiePixels) && !empty($this->unqiePixels)) {
if (array_search($screenWidth . "×" . $screenHeight . "@" . $this->fingerprintData['devicePixelRatio'], $this->unqiePixels) === false) {
if (array_search($screenWidth . "×" . $screenHeight, $this->unqiePixels) === false) {
$this->riskLevel += 10;
$this->riskFactors[] = "非常见分辨率";
}
}
}
// 检测设备像素比异常
if (isset($this->fingerprintData['devicePixelRatio']) &&
($this->fingerprintData['devicePixelRatio'] < 0.5 || $this->fingerprintData['devicePixelRatio'] > 5)) {
$this->riskLevel += 20;
$this->riskFactors[] = "异常设备像素比: " . $this->fingerprintData['devicePixelRatio'];
}
// 初始状态下,视口不重合或有缩放,几乎不可能是机器人
if (isset($this->fingerprintData['scale'], $this->fingerprintData['visualViewportOffset']) &&
is_array($this->fingerprintData['visualViewportOffset']) &&
($this->fingerprintData['scale'] != 1 || $this->fingerprintData['visualViewportOffset'][0] != 0)) {
$this->riskLevel -= 10;
$this->riskFactors[] = '初始状态下,视口不重合或有缩放,几乎不可能是机器人';
}
}
// 检查浏览器特性支持是否合理
private function checkBrowserFeatures() {
$deviceType = $this->getDeviceType();
// 检查WebGL和WebGPU支持是否匹配现代浏览器
$webglSupported = !empty ($this->fingerprintData['webgl']);
$isModernBrowser = preg_match('/chrome\/(80|8[1-9]|9[0-9]|1[0-9]{2})|firefox\/(75|7[6-9]|8[0-9]|9[0-9])/i', $this->fingerprintData['userAgent'] ?? '');
if ($isModernBrowser && !$webglSupported) {
$this->riskLevel += 10;
$this->riskFactors[] = "现代浏览器但不支持WebGL,可能是受限环境";
}
// 检查WebRTC支持
if (empty($this->fingerprintData['experimental'][0]['isWebRTCSupported'])) {
$this->riskLevel += 10;
$this->riskFactors[] = "不支持WebRTC,可能是自动化环境";
}
// 检查WebGPU支持
$webGPUSupported = !empty ($this->fingerprintData['webGPU']);
if ($webGPUSupported) {
$webGPU = $this->fingerprintData['webGpuData'];
if ($webGPU['adapterInfo'] && isset($webGPU['adapterInfo']['description']) && strpos($webGPU['adapterInfo']['description'], 'SwiftShader') !== false) {
$this->riskLevel += 50; // 强信号:软件渲染
$this->riskFactors[] = '软件渲染';
} else if ($webGPU['adapterInfo'] && isset($webGPU['adapterInfo']['description']) && preg_match('/(VMware|VirtualBox|Parallels)/i', $webGPU['adapterInfo']['description'])) {
$this->riskLevel += 30; // 强信号:虚拟机
$this->riskFactors[] = '虚拟机';
}
// 基准测试时间过长 (例如超过500ms,具体阈值需要测试确定)
if ($webGPU['benchmark']['durationMs'] > 500) {
$this->riskLevel += 50;
$this->riskFactors[] = '渲染过慢';
}
} else {
if( $deviceType == 'desktop' ) {
$this->riskLevel += 15;
$this->riskFactors[] = 'PC不支持WEBGPU';
}
}
}
// 检查语言异常
private function checkLanguages() {
if (empty($this->fingerprintData['languages']) || empty($this->fingerprintData['language'])) return;
// 检测国家与时区是否匹配
$supportLangs = $this->getSupportLang($this->fingerprintData['ciso']);
$intersect = !empty(array_intersect($supportLangs, array_map('strtolower', $this->fingerprintData['languages'])));
if( ! $intersect ) {
$this->riskLevel += 10;
$this->riskFactors[] = "语言不属于IP所在国家: " . join('; ', $supportLangs);
}
}
// 检查时区异常
private function checkTimezoneAnomalies() {
if (empty($this->fingerprintData['timezone']) || empty($this->fingerprintData['timezoneOffset'])) return;
// 根据时区偏移计算预期时区
$offsetHours = $this->fingerprintData['timezoneOffset'] / 60 * -1;
$expectedTimezones = $this->getTimezonesByOffset($offsetHours);
// 检查报告的时区是否与偏移量匹配
$found = false;
foreach ($expectedTimezones as $tz) {
if (stripos($this->fingerprintData['timezone'], $tz) !== false) {
$found = true;
break;
}
}
if (!$found) {
$this->riskLevel += 5;
$this->riskFactors[] = "时区与偏移量不匹配,可能是伪造的环境";
}
// 检测时区异常
$timezoneOffset = abs($this->fingerprintData['timezoneOffset']);
if ($timezoneOffset % 60 !== 0 && $timezoneOffset % 60 !== 30 && $timezoneOffset % 60 !== 45) {
$this->riskLevel += 10;
$this->riskFactors[] = "异常时区偏移: " . $timezoneOffset . "分钟";
}
// 检测国家与时区是否匹配
$supportTZ = $this->getSupportTZ($this->fingerprintData['ciso']);
if( ! in_array(strtolower($this->fingerprintData['timezone']), $supportTZ) ) {
$this->riskLevel += 10;
$this->riskFactors[] = "时区不属于IP所在国家: " . join('; ', $supportTZ);
}
}
// 根据时区偏移获取可能的时区
private function getTimezonesByOffset($offset) {
$timezones = [
'-12' => ['Pacific/Kwajalein'],
'-11' => ['Pacific/Midway', 'Pacific/Niue', 'Pacific/Pago_Pago'],
'-10' => ['Pacific/Honolulu', 'Pacific/Rarotonga', 'Pacific/Tahiti'],
'-9' => ['America/Anchorage', 'Pacific/Gambier'],
'-8.5' => ['Pacific/Marquesas'],
'-8' => ['America/Vancouver', 'America/Tijuana', 'America/Los_Angeles', 'Pacific/Pitcairn', 'America/Whitehorse'],
'-7' => [ 'America/Dawson_Creek', 'America/Denver', 'America/Phoenix', 'America/Edmonton', 'America/Hermosillo', 'America/Mazatlan', 'America/Yellowknife'],
'-6' => ['America/Chicago', 'America/Winnipeg', 'America/Belize', 'America/Mexico_City', 'America/Chihuahua', 'America/Costa_Rica', 'America/Regina', 'America/El_Salvador', 'America/Tegucigalpa', 'America/Managua', 'America/Guatemala', 'Pacific/Galapagos'],
'-5' => ['America/New_York', 'America/Bogota', 'America/Nassau', 'America/Lima', 'America/Jamaica', 'America/Iqaluit', 'America/Havana', 'America/Guayaquil', 'America/Cayman', 'America/Cancun', 'Pacific/Easter', 'America/Toronto', 'America/Rio_Branco', 'America/Port-au-Prince', 'America/Panama'],
'-4' => ['America/Curacao','America/Martinique','America/Manaus','America/La_Paz','America/Halifax','America/Guyana','America/Grand_Turk','America/Port_of_Spain','America/Santo_Domingo','America/Thule','America/Boa_Vista','America/Barbados','Atlantic/Bermuda','America/Puerto_Rico','America/Porto_Velho'],
'-3.5' => ['America/Caracas'],
'-3' => ['Atlantic/Azores', 'Antarctica/Palmer', 'America/Cuiaba', 'America/Santiago', 'America/Recife', 'America/Paramaribo', 'America/Montevideo', 'America/Miquelon', 'America/Maceio', 'America/Godthab', 'America/Fortaleza', 'America/Cayenne', 'America/Belem', 'America/Bahia', 'America/Argentina/Buenos_Aires', 'America/Araguaina', 'Atlantic/Stanley', 'America/Asuncion', 'America/Campo_Grande', 'Antarctica/Rothera'],
'-2' => ['America/Sao_Paulo', 'Atlantic/South_Georgia', 'America/St_Johns', 'America/Noronha'],
'-1' => ['Atlantic/Cape_Verde', 'Atlantic/Azores', 'America/Scoresbysund'],
'0' => ['UTC', 'Europe/London', 'Africa/Bissau', 'Etc/Greenwich', 'Atlantic/Reykjavik', 'Atlantic/Faroe', 'Atlantic/Canary', 'America/Danmarkshavn', 'Africa/Monrovia', 'Africa/El_Aaiun', 'Africa/Casablanca', 'Europe/Dublin', 'Africa/Accra', 'Africa/Abidjan', 'Europe/Lisbon'],
'1' => ['Europe/Paris', 'Africa/Lagos', 'Europe/Prague', 'Europe/Rome', 'Europe/Stockholm', 'Africa/Algiers', 'Europe/Vienna', 'Europe/Warsaw', 'Europe/Zurich', 'Africa/Ceuta', 'Africa/Ndjamena', 'Africa/Tunis', 'Europe/Tirane', 'Europe/Amsterdam', 'Europe/Andorra', 'Europe/Belgrade', 'Europe/Berlin', 'Europe/Brussels', 'Europe/Budapest', 'Europe/Copenhagen', 'Europe/Gibraltar', 'Europe/Luxembourg', 'Europe/Madrid', 'Europe/Malta', 'Europe/Monaco', 'Europe/Oslo'],
'2' => ['Europe/Istanbul', 'Africa/Cairo', 'Asia/Jerusalem', 'Europe/Vilnius', 'Europe/Tallinn', 'Africa/Johannesburg', 'Africa/Maputo', 'Africa/Tripoli', 'Asia/Amman', 'Asia/Beirut', 'Asia/Damascus', 'Asia/Gaza', 'Africa/Windhoek', 'Asia/Nicosia', 'Europe/Athens', 'Europe/Bucharest', 'Europe/Chisinau', 'Europe/Helsinki', 'Europe/Kaliningrad', 'Europe/Kiev', 'Europe/Riga', 'Europe/Sofia'],
'3' => ['Europe/Moscow', 'Asia/Riyadh', 'Europe/Minsk', 'Asia/Qatar', 'Asia/Baghdad', 'Antarctica/Syowa', 'Africa/Nairobi', 'Africa/Khartoum'],
'3.5' => ['Asia/Tehran'],
'4' => ['Asia/Dubai', 'Asia/Baku', 'Indian/Mauritius', 'Indian/Mahe', 'Europe/Samara', 'Indian/Reunion', 'Asia/Tbilisi', 'Asia/Yerevan'],
'4.5' => ['Asia/Kabul'],
'5'=> ['Asia/Karachi', 'Asia/Tashkent', 'Indian/Kerguelen', 'Asia/Yekaterinburg', 'Asia/Dushanbe', 'Asia/Ashgabat', 'Asia/Aqtobe', 'Asia/Aqtau', 'Indian/Maldives', 'Antarctica/Mawson'],
'5.5' => ['Asia/Calcutta', 'Asia/Colombo', 'Asia/Kolkata'],
'5.75' => ['Asia/Kathmandu'],
'6' => ['Indian/Chagos', 'Asia/Thimphu', 'Asia/Dhaka', 'Asia/Bishkek', 'Asia/Almaty', 'Antarctica/Vostok', 'Asia/Omsk'],
'6.5' => ['Asia/Rangoon', 'Indian/Cocos'],
'7' => ['Asia/Bangkok', 'Indian/Christmas', 'Asia/Saigon', 'Asia/Krasnoyarsk', 'Asia/Jakarta', 'Asia/Hovd', 'Antarctica/Davis'],
'8' => ['Asia/Choibalsan', 'Asia/Taipei', 'Asia/Singapore', 'Australia/Perth', 'Asia/Manila', 'Asia/Makassar', 'Asia/Macau', 'Asia/Kuala_Lumpur', 'Asia/Irkutsk', 'Asia/Hong_Kong', 'Asia/Ulaanbaatar', 'Asia/Brunei', 'Antarctica/Casey', 'Asia/Shanghai', 'Asia/Beijing'],
'8.5' => ['Asia/Pyongyang'],
'9' => ['Asia/Yakutsk', 'Asia/Tokyo', 'Asia/Seoul', 'Asia/Jayapura', 'Asia/Dili', 'Pacific/Palau'],
'9.5' => ['Australia/Darwin'],
'10' => ['Pacific/Guam', 'Pacific/Chuuk', 'Asia/Vladivostok', 'Asia/Magadan', 'Antarctica/DumontDUrville', 'Pacific/Port_Moresby', 'Australia/Brisbane'],
'10.5' => ['Australia/Adelaide'],
'11' => ['Australia/Sydney', 'Asia/Magadan', 'Pacific/Noumea', 'Pacific/Kosrae', 'Pacific/Guadalcanal', 'Pacific/Efate', 'Pacific/Pohnpei', 'Australia/Hobart', 'Pacific/Norfolk'],
'12' => ['Pacific/Funafuti', 'Pacific/Nauru', 'Pacific/Tarawa', 'Pacific/Wake', 'Asia/Kamchatka', 'Pacific/Kwajalein', 'Pacific/Majuro', 'Pacific/Wallis'],
'13' => ['Pacific/Enderbury', 'Pacific/Tongatapu', 'Pacific/Fakaofo', 'Pacific/Auckland', 'Pacific/Fiji']
];
return isset($timezones["$offset"]) ? $timezones["$offset"] : [];
}
// 根据国家获取一般设置的语言
private function getSupportLang($country_code) {
$browserLanguages = [
"AF" => ["ps-af", "fa-af", "uz-af"], // 阿富汗: 普什图语, 达里语, 乌兹别克语
"AL" => ["sq-al", "sq"], // 阿尔巴尼亚: 阿尔巴尼亚语
"DZ" => ["ar-dz", "fr-dz", "ar", "fr"], // 阿尔及利亚: 阿拉伯语, 法语
"AD" => ["ca-ad", "ca"], // 安道尔: 加泰罗尼亚语
"AO" => ["pt-ao", "pt"], // 安哥拉: 葡萄牙语
"AR" => ["es-ar", "es"], // 阿根廷: 西班牙语
"AM" => ["hy-am", "hy"], // 亚美尼亚: 亚美尼亚语
"AU" => ["en-au", "en"], // 澳大利亚: 英语
"AT" => ["de-at", "de"], // 奥地利: 德语
"AZ" => ["az-latn-az", "az"], // 阿塞拜疆: 阿塞拜疆语 (拉丁文)
"BH" => ["ar-bh", "ar"], // 巴林: 阿拉伯语
"BD" => ["bn-bd", "bn"], // 孟加拉国: 孟加拉语
"BY" => ["be-by", "ru-by", "be", "ru"], // 白俄罗斯: 白俄罗斯语, 俄语
"BE" => ["nl-be", "fr-be", "de-be", "nl", "fr", "de"], // 比利时: 荷兰语, 法语, 德语
"BZ" => ["en-bz", "es-bz", "en", "es"], // 伯利兹: 英语, 西班牙语
"BO" => ["es-bo", "qu-bo", "ay-bo", "es", "qu", "ay"], // 玻利维亚: 西班牙语, 盖丘亚语, 艾马拉语
"BA" => ["bs-ba", "hr-ba", "sr-ba", "bs", "hr", "sr"], // 波斯尼亚和黑塞哥维那: 波斯尼亚语, 克罗地亚语, 塞尔维亚语
"BR" => ["pt-br", "pt"], // 巴西: 葡萄牙语
"BN" => ["ms-bn", "ms"], // 文莱: 马来语
"BG" => ["bg-bg", "bg"], // 保加利亚: 保加利亚语
"KH" => ["km-kh", "km"], // 柬埔寨: 高棉语
"CA" => ["en-ca", "fr-ca", "en", "fr"], // 加拿大: 英语, 法语
"CL" => ["es-cl", "es"], // 智利: 西班牙语
"CN" => ["zh-cn", "zh"], // 中国大陆: 简体中文
"CO" => ["es-co", "es"], // 哥伦比亚: 西班牙语
"CR" => ["es-cr", "es"], // 哥斯达黎加: 西班牙语
"HR" => ["hr-hr", "hr"], // 克罗地亚: 克罗地亚语
"CU" => ["es-cu", "es"], // 古巴: 西班牙语
"CY" => ["el-cy", "tr-cy", "el", "tr"], // 塞浦路斯: 希腊语, 土耳其语
"CZ" => ["cs-cz", "cs"], // 捷克共和国: 捷克语
"DK" => ["da-dk", "da"], // 丹麦: 丹麦语
"DO" => ["es-do", "es"], // 多米尼加共和国: 西班牙语
"EC" => ["es-ec", "es"], // 厄瓜多尔: 西班牙语
"EG" => ["ar-eg", "ar"], // 埃及: 阿拉伯语
"SV" => ["es-sv", "es"], // 萨尔瓦多: 西班牙语
"EE" => ["et-ee", "et"], // 爱沙尼亚: 爱沙尼亚语
"ET" => ["am-et", "om-et", "ti-et", "am", "om", "ti"], // 埃塞俄比亚: 阿姆哈拉语, 奥罗莫语, 提格里尼亚语
"FI" => ["fi-fi", "sv-fi", "fi", "sv"], // 芬兰: 芬兰语, 瑞典语
"FR" => ["fr-fr", "fr"], // 法国: 法语
"GE" => ["ka-ge", "ka"], // 格鲁吉亚: 格鲁吉亚语
"DE" => ["de-de", "de"], // 德国: 德语
"GR" => ["el-gr", "el"], // 希腊: 希腊语
"GT" => ["es-gt", "es"], // 危地马拉: 西班牙语
"HN" => ["es-hn", "es"], // 洪都拉斯: 西班牙语
"HK" => ["zh-hk", "en-hk", "zh-tw", "zh", "en"], // 香港: 繁体中文, 英语
"HU" => ["hu-hu", "hu"], // 匈牙利: 匈牙利语
"IS" => ["is-is", "is"], // 冰岛: 冰岛语
"IN" => ["hi-in", "en-in", "ta-in", "te-in", "bn-in", "hi", "en", "ta", "te", "bn"], // 印度: 印地语, 英语, 泰米尔语, 泰卢固语, 孟加拉语
"ID" => ["id-id", "id"], // 印度尼西亚: 印度尼西亚语
"IR" => ["fa-ir", "fa"], // 伊朗: 波斯语
"IQ" => ["ar-iq", "ku-iq", "ar", "ku"], // 伊拉克: 阿拉伯语, 库尔德语
"IE" => ["en-ie", "ga-ie", "en", "ga"], // 爱尔兰: 英语, 爱尔兰语
"IL" => ["he-il", "ar-il", "en-il", "he", "ar", "en"], // 以色列: 希伯来语, 阿拉伯语, 英语
"IT" => ["it-it", "de-it", "it", "de"], // 意大利: 意大利语, 德语
"JM" => ["en-jm", "en"], // 牙买加: 英语
"JP" => ["ja-jp", "ja"], // 日本: 日语
"JO" => ["ar-jo", "ar"], // 约旦: 阿拉伯语
"KZ" => ["kk-kz", "ru-kz", "kk", "ru"], // 哈萨克斯坦: 哈萨克语, 俄语
"KE" => ["sw-ke", "en-ke", "sw", "en"], // 肯尼亚: 斯瓦希里语, 英语
"KR" => ["ko-kr", "ko"], // 韩国: 韩语
"KW" => ["ar-kw", "ar"], // 科威特: 阿拉伯语
"LA" => ["lo-la", "lo"], // 老挝: 老挝语
"LV" => ["lv-lv", "lv"], // 拉脱维亚: 拉脱维亚语
"LB" => ["ar-lb", "fr-lb", "ar", "fr"], // 黎巴嫩: 阿拉伯语, 法语
"LI" => ["de-li", "de"], // 列支敦士登: 德语
"LT" => ["lt-lt", "lt"], // 立陶宛: 立陶宛语
"LU" => ["fr-lu", "de-lu", "lb-lu", "fr", "de", "lb"], // 卢森堡: 法语, 德语, 卢森堡语
"MO" => ["zh-mo", "pt-mo", "zh-hk", "zh", "pt"], // 澳门: 中文, 葡萄牙语
"MY" => ["ms-my", "en-my", "zh-my", "ms", "en", "zh"], // 马来西亚: 马来语, 英语, 中文
"MX" => ["es-mx", "es"], // 墨西哥: 西班牙语
"MD" => ["ro-md", "ru-md", "ro", "ru"], // 摩尔多瓦: 罗马尼亚语, 俄语
"MC" => ["fr-mc", "it-mc", "fr", "it"], // 摩纳哥: 法语, 意大利语
"MN" => ["mn-mn", "mn"], // 蒙古: 蒙古语
"MA" => ["ar-ma", "fr-ma", "ar", "fr"], // 摩洛哥: 阿拉伯语, 法语
"NL" => ["nl-nl", "fy-nl", "nl", "fy"], // 荷兰: 荷兰语, 弗里斯语
"NZ" => ["en-nz", "mi-nz", "en", "mi"], // 新西兰: 英语, 毛利语
"NI" => ["es-ni", "es"], // 尼加拉瓜: 西班牙语
"NG" => ["en-ng", "ha-ng", "ig-ng", "yo-ng", "en", "ha", "ig", "yo"], // 尼日利亚: 英语, 豪萨语, 伊博语, 约鲁巴语
"NO" => ["nb-no", "nn-no", "se-no", "nb", "nn", "se"], // 挪威: 书面挪威语, 新挪威语, 萨米语
"OM" => ["ar-om", "ar"], // 阿曼: 阿拉伯语
"PK" => ["ur-pk", "en-pk", "pa-pk", "ur", "en", "pa"], // 巴基斯坦: 乌尔都语, 英语, 旁遮普语
"PA" => ["es-pa", "es"], // 巴拿马: 西班牙语
"PY" => ["es-py", "gn-py", "es", "gn"], // 巴拉圭: 西班牙语, 瓜拉尼语
"PE" => ["es-pe", "qu-pe", "ay-pe", "es", "qu", "ay"], // 秘鲁: 西班牙语, 盖丘亚语, 艾马拉语
"PH" => ["en-ph", "fil-ph", "en", "fil"], // 菲律宾: 英语, 菲律宾语
"PL" => ["pl-pl", "pl"], // 波兰: 波兰语
"PT" => ["pt-pt", "pt"], // 葡萄牙: 葡萄牙语
"PR" => ["es-pr", "en-pr", "es", "en"], // 波多黎各: 西班牙语, 英语
"QA" => ["ar-qa", "ar"], // 卡塔尔: 阿拉伯语
"RO" => ["ro-ro", "ro"], // 罗马尼亚: 罗马尼亚语
"RU" => ["ru-ru", "tt-ru", "ru", "tt"], // 俄罗斯: 俄语, 鞑靼语
"SA" => ["ar-sa", "ar"], // 沙特阿拉伯: 阿拉伯语
"RS" => ["sr-cyrl-rs", "sr-latn-rs", "sr"], // 塞尔维亚: 塞尔维亚语 (西里尔文, 拉丁文)
"SG" => ["en-sg", "zh-sg", "ms-sg", "ta-sg", "en", "zh", "ms", "ta"], // 新加坡: 英语, 中文, 马来语, 泰米尔语
"SK" => ["sk-sk", "sk"], // 斯洛伐克: 斯洛伐克语
"SI" => ["sl-si", "sl"], // 斯洛文尼亚: 斯洛文尼亚语
"ZA" => ["en-za", "af-za", "zu-za", "xh-za", "en", "af", "zu", "xh"], // 南非: 英语, 南非荷兰语, 祖鲁语, 科萨语
"ES" => ["es-es", "ca-es", "gl-es", "eu-es", "es", "ca", "gl", "eu"], // 西班牙: 西班牙语, 加泰罗尼亚语, 加利西亚语, 巴斯克语
"LK" => ["si-lk", "ta-lk", "si", "ta"], // 斯里兰卡: 僧伽罗语, 泰米尔语
"SE" => ["sv-se", "sv"], // 瑞典: 瑞典语
"CH" => ["de-ch", "fr-ch", "it-ch", "rm-ch", "de", "fr", "it", "rm"], // 瑞士: 德语, 法语, 意大利语, 罗曼什语
"SY" => ["ar-sy", "ar"], // 叙利亚: 阿拉伯语
"TW" => ["zh-tw", "zh"], // 台湾: 繁体中文
"TH" => ["th-th", "th"], // 泰国: 泰语
"TT" => ["en-tt", "en"], // 特立尼达和多巴哥: 英语
"TN" => ["ar-tn", "fr-tn", "ar", "fr"], // 突尼斯: 阿拉伯语, 法语
"TR" => ["tr-tr", "tr"], // 土耳其: 土耳其语
"UA" => ["uk-ua", "ru-ua", "uk", "ru"], // 乌克兰: 乌克兰语, 俄语
"AE" => ["ar-ae", "en-ae", "ar", "en"], // 阿拉伯联合酋长国: 阿拉伯语, 英语
"GB" => ["en-gb", "cy-gb", "gd-gb", "en", "cy", "gd"], // 英国: 英语, 威尔士语, 苏格兰盖尔语
"US" => ["en-us", "es-us", "en", "es"], // 美国: 英语, 西班牙语
"UY" => ["es-uy", "es"], // 乌拉圭: 西班牙语
"UZ" => ["uz-latn-uz", "ru-uz", "uz", "ru"], // 乌兹别克斯坦: 乌兹别克语 (拉丁文), 俄语
"VE" => ["es-ve", "es"], // 委内瑞拉: 西班牙语
"VN" => ["vi-vn", "vi"], // 越南: 越南语
"YE" => ["ar-ye", "ar"], // 也门: 阿拉伯语
"ZW" => ["en-zw", "sn-zw", "nd-zw", "en", "sn", "nd"], // 津巴布韦: 英语, 修纳语, 恩德贝勒语
];
return isset($browserLanguages[$country_code]) ? $browserLanguages[$country_code] : [];
}
// 根据国家获取一般设置的时区
private function getSupportTZ($country_code) {
$countryTimezoneMap = [
'AD' => ['Europe/Andorra'],
'AE' => ['Asia/Dubai'],
'AF' => ['Asia/Kabul'],
'AG' => ['America/Antigua'],
'AI' => ['America/Anguilla'],
'AL' => ['Europe/Tirane'],
'AM' => ['Asia/Yerevan'],
'AO' => ['Africa/Luanda'],
'AQ' => ['Antarctica/Casey', 'Antarctica/Davis', 'Antarctica/DumontDUrville', 'Antarctica/Macquarie', 'Antarctica/Mawson', 'Antarctica/McMurdo', 'Antarctica/Palmer', 'Antarctica/Rothera', 'Antarctica/South_Pole', 'Antarctica/Syowa', 'Antarctica/Troll', 'Antarctica/Vostok'],
'AR' => ['America/Argentina/Buenos_Aires', 'America/Argentina/Catamarca', 'America/Argentina/ComodRivadavia', 'America/Argentina/Cordoba', 'America/Argentina/Jujuy', 'America/Argentina/La_Rioja', 'America/Argentina/Mendoza', 'America/Argentina/Rio_Gallegos', 'America/Argentina/Salta', 'America/Argentina/San_Juan', 'America/Argentina/San_Luis', 'America/Argentina/Tucuman', 'America/Argentina/Ushuaia', 'America/Buenos_Aires', 'America/Catamarca', 'America/Cordoba', 'America/Jujuy', 'America/Mendoza', 'America/Rosario'],
'AS' => ['Pacific/Pago_Pago', 'Pacific/Samoa', 'US/Samoa'],
'AT' => ['Europe/Vienna'],
'AU' => ['Australia/ACT', 'Australia/Adelaide', 'Australia/Brisbane', 'Australia/Broken_Hill', 'Australia/Canberra', 'Australia/Currie', 'Australia/Darwin', 'Australia/Eucla', 'Australia/Hobart', 'Australia/LHI', 'Australia/Lindeman', 'Australia/Lord_Howe', 'Australia/Melbourne', 'Australia/NSW', 'Australia/North', 'Australia/Perth', 'Australia/Queensland', 'Australia/South', 'Australia/Sydney', 'Australia/Tasmania', 'Australia/Victoria', 'Australia/West', 'Australia/Yancowinna'],
'AW' => ['America/Aruba'],
'AX' => ['Europe/Mariehamn'],
'AZ' => ['Asia/Baku'],
'BA' => ['Europe/Sarajevo'],
'BB' => ['America/Barbados'],
'BD' => ['Asia/Dacca', 'Asia/Dhaka'],
'BE' => ['Europe/Brussels'],
'BF' => ['Africa/Ouagadougou'],
'BG' => ['Europe/Sofia'],
'BH' => ['Asia/Bahrain'],
'BI' => ['Africa/Bujumbura'],
'BJ' => ['Africa/Porto-Novo'],
'BL' => ['America/St_Barthelemy'],
'BM' => ['Atlantic/Bermuda'],
'BN' => ['Asia/Brunei'],
'BO' => ['America/La_Paz'],
'BQ' => ['America/Kralendijk'],
'BR' => ['America/Araguaina', 'America/Bahia', 'America/Belem', 'America/Boa_Vista', 'America/Campo_Grande', 'America/Cuiaba', 'America/Eirunepe', 'America/Fortaleza', 'America/Maceio', 'America/Manaus', 'America/Noronha', 'America/Porto_Acre', 'America/Porto_Velho', 'America/Recife', 'America/Rio_Branco', 'America/Santarem', 'America/Sao_Paulo', 'Brazil/Acre', 'Brazil/DeNoronha', 'Brazil/East', 'Brazil/West'],
'BS' => ['America/Nassau'],
'BT' => ['Asia/Thimphu'],
'BW' => ['Africa/Gaborone'],
'BY' => ['Europe/Minsk'],
'BZ' => ['America/Belize'],
'CA' => ['America/Atikokan', 'America/Blanc-Sablon', 'America/Cambridge_Bay', 'America/Coral_Harbour', 'America/Creston', 'America/Dawson', 'America/Dawson_Creek', 'America/Edmonton', 'America/Fort_Nelson', 'America/Glace_Bay', 'America/Goose_Bay', 'America/Halifax', 'America/Inuvik', 'America/Iqaluit', 'America/Moncton', 'America/Montreal', 'America/Nipigon', 'America/Pangnirtung', 'America/Rainy_River', 'America/Rankin_Inlet', 'America/Regina', 'America/Resolute', 'America/St_Johns', 'America/Swift_Current', 'America/Thunder_Bay', 'America/Toronto', 'America/Vancouver', 'America/Whitehorse', 'America/Winnipeg', 'America/Yellowknife', 'Canada/Atlantic', 'Canada/Central', 'Canada/Eastern', 'Canada/Mountain', 'Canada/Newfoundland', 'Canada/Pacific', 'Canada/Saskatchewan', 'Canada/Yukon'],
'CC' => ['Indian/Cocos'],
'CD' => ['Africa/Kinshasa', 'Africa/Lubumbashi'],
'CF' => ['Africa/Bangui'],
'CG' => ['Africa/Brazzaville'],
'CH' => ['Europe/Zurich'],
'CI' => ['Africa/Abidjan'],
'CK' => ['Pacific/Rarotonga'],
'CL' => ['America/Punta_Arenas', 'America/Santiago', 'Chile/Continental', 'Chile/EasterIsland', 'Pacific/Easter'],
'CM' => ['Africa/Douala'],
'CN' => ['Asia/Chongqing', 'Asia/Chungking', 'Asia/Harbin', 'Asia/Kashgar', 'Asia/Shanghai', 'Asia/Urumqi', 'PRC'],
'CO' => ['America/Bogota'],
'CR' => ['America/Costa_Rica'],
'CU' => ['America/Havana', 'Cuba'],
'CV' => ['Atlantic/Cape_Verde'],
'CW' => ['America/Curacao'],
'CX' => ['Indian/Christmas'],
'CY' => ['Asia/Famagusta', 'Asia/Nicosia', 'Europe/Nicosia'],
'CZ' => ['Europe/Prague'],
'DE' => ['Europe/Berlin', 'Europe/Busingen'],
'DJ' => ['Africa/Djibouti'],
'DK' => ['Europe/Copenhagen'],
'DM' => ['America/Dominica'],
'DO' => ['America/Santo_Domingo'],
'DZ' => ['Africa/Algiers'],
'EC' => ['America/Guayaquil', 'Pacific/Galapagos'],
'EE' => ['Europe/Tallinn'],
'EG' => ['Africa/Cairo', 'Egypt'],
'EH' => ['Africa/El_Aaiun'],
'ER' => ['Africa/Asmara'],
'ES' => ['Africa/Ceuta', 'Atlantic/Canary', 'Europe/Madrid'],
'ET' => ['Africa/Addis_Ababa'],
'FI' => ['Europe/Helsinki'],
'FJ' => ['Pacific/Fiji'],
'FK' => ['Atlantic/Stanley'],
'FM' => ['Pacific/Chuuk', 'Pacific/Kosrae', 'Pacific/Pohnpei', 'Pacific/Ponape', 'Pacific/Truk', 'Pacific/Yap'],
'FO' => ['Atlantic/Faeroe', 'Atlantic/Faroe'],
'FR' => ['Europe/Paris'],
'GA' => ['Africa/Libreville'],
'GB' => ['Europe/Belfast', 'Europe/London', 'GB', 'GB-Eire'],
'GD' => ['America/Grenada'],
'GE' => ['Asia/Tbilisi'],
'GF' => ['America/Cayenne'],
'GG' => ['Europe/Guernsey'],
'GH' => ['Africa/Accra'],
'GI' => ['Europe/Gibraltar'],
'GL' => ['America/Danmarkshavn', 'America/Godthab', 'America/Nuuk', 'America/Scoresbysund', 'America/Thule'],
'GM' => ['Africa/Banjul'],
'GN' => ['Africa/Conakry'],
'GP' => ['America/Guadeloupe'],
'GQ' => ['Africa/Malabo'],
'GR' => ['Europe/Athens'],
'GS' => ['Atlantic/South_Georgia'],
'GT' => ['America/Guatemala'],
'GU' => ['Pacific/Guam'],
'GW' => ['Africa/Bissau'],
'GY' => ['America/Guyana'],
'HK' => ['Asia/Hong_Kong', 'Hongkong'],
'HN' => ['America/Tegucigalpa'],
'HR' => ['Europe/Zagreb'],
'HT' => ['America/Port-au-Prince'],
'HU' => ['Europe/Budapest'],
'ID' => ['Asia/Jakarta', 'Asia/Jayapura', 'Asia/Makassar', 'Asia/Pontianak', 'Asia/Ujung_Pandang'],
'IE' => ['Eire', 'Europe/Dublin'],
'IL' => ['Asia/Jerusalem', 'Asia/Tel_Aviv', 'Israel'],
'IM' => ['Europe/Isle_of_Man'],
'IN' => ['Asia/Calcutta', 'Asia/Kolkata'],
'IO' => ['Indian/Chagos'],
'IQ' => ['Asia/Baghdad'],
'IR' => ['Asia/Tehran', 'Iran'],
'IS' => ['Atlantic/Reykjavik', 'Iceland'],
'IT' => ['Europe/Rome'],
'JE' => ['Europe/Jersey'],
'JM' => ['America/Jamaica', 'Jamaica'],
'JO' => ['Asia/Amman'],
'JP' => ['Asia/Tokyo', 'Japan'],
'KE' => ['Africa/Nairobi'],
'KG' => ['Asia/Bishkek'],
'KH' => ['Asia/Phnom_Penh'],
'KI' => ['Pacific/Enderbury', 'Pacific/Kanton', 'Pacific/Kiritimati', 'Pacific/Tarawa'],
'KM' => ['Indian/Comoro'],
'KN' => ['America/St_Kitts'],
'KP' => ['Asia/Pyongyang'],
'KR' => ['Asia/Seoul', 'ROK'],
'KW' => ['Asia/Kuwait'],
'KY' => ['America/Cayman'],
'KZ' => ['Asia/Almaty', 'Asia/Aqtau', 'Asia/Aqtobe', 'Asia/Atyrau', 'Asia/Oral', 'Asia/Qostanay', 'Asia/Qyzylorda'],
'LA' => ['Asia/Vientiane'],
'LB' => ['Asia/Beirut'],
'LC' => ['America/St_Lucia'],
'LI' => ['Europe/Vaduz'],
'LK' => ['Asia/Colombo'],
'LR' => ['Africa/Monrovia'],
'LS' => ['Africa/Maseru'],
'LT' => ['Europe/Vilnius'],
'LU' => ['Europe/Luxembourg'],
'LV' => ['Europe/Riga'],
'LY' => ['Africa/Tripoli', 'Libya'],
'MA' => ['Africa/Casablanca'],
'MC' => ['Europe/Monaco'],
'MD' => ['Europe/Chisinau', 'Europe/Tiraspol'],
'ME' => ['Europe/Podgorica'],
'MF' => ['America/Marigot'],
'MG' => ['Indian/Antananarivo'],
'MH' => ['Kwajalein', 'Pacific/Kwajalein', 'Pacific/Majuro'],
'MK' => ['Europe/Skopje'],
'ML' => ['Africa/Bamako', 'Africa/Timbuktu'],
'MM' => ['Asia/Rangoon', 'Asia/Yangon'],
'MN' => ['Asia/Choibalsan', 'Asia/Hovd', 'Asia/Ulaanbaatar', 'Asia/Ulan_Bator'],
'MO' => ['Asia/Macao', 'Asia/Macau'],
'MP' => ['Pacific/Saipan'],
'MQ' => ['America/Martinique'],
'MR' => ['Africa/Nouakchott'],
'MS' => ['America/Montserrat'],
'MT' => ['Europe/Malta'],
'MU' => ['Indian/Mauritius'],
'MV' => ['Indian/Maldives'],
'MW' => ['Africa/Blantyre'],
'MX' => ['America/Bahia_Banderas', 'America/Cancun', 'America/Chihuahua', 'America/Ciudad_Juarez', 'America/Ensenada', 'America/Hermosillo', 'America/Matamoros', 'America/Mazatlan', 'America/Merida', 'America/Mexico_City', 'America/Monterrey', 'America/Ojinaga', 'America/Santa_Isabel', 'America/Tijuana', 'Mexico/BajaNorte', 'Mexico/BajaSur', 'Mexico/General'],
'MY' => ['Asia/Kuala_Lumpur', 'Asia/Kuching'],
'MZ' => ['Africa/Maputo'],
'NA' => ['Africa/Windhoek'],
'NC' => ['Pacific/Noumea'],
'NE' => ['Africa/Niamey'],
'NF' => ['Pacific/Norfolk'],
'NG' => ['Africa/Lagos'],
'NI' => ['America/Managua'],
'NL' => ['Europe/Amsterdam'],
'NO' => ['Europe/Oslo'],
'NP' => ['Asia/Kathmandu', 'Asia/Katmandu'],
'NR' => ['Pacific/Nauru'],
'NU' => ['Pacific/Niue'],
'NZ' => ['Antarctica/McMurdo', 'Antarctica/South_Pole', 'NZ', 'NZ-CHAT', 'Pacific/Auckland', 'Pacific/Chatham'],
'OM' => ['Asia/Muscat'],
'PA' => ['America/Panama'],
'PE' => ['America/Lima'],
'PF' => ['Pacific/Gambier', 'Pacific/Marquesas', 'Pacific/Tahiti'],
'PG' => ['Pacific/Bougainville', 'Pacific/Port_Moresby'],
'PH' => ['Asia/Manila'],
'PK' => ['Asia/Karachi'],
'PL' => ['Europe/Warsaw', 'Poland'],
'PM' => ['America/Miquelon'],
'PN' => ['Pacific/Pitcairn'],
'PR' => ['America/Puerto_Rico'],
'PS' => ['Asia/Gaza', 'Asia/Hebron'],
'PT' => ['Atlantic/Azores', 'Atlantic/Madeira', 'Europe/Lisbon', 'Portugal'],
'PW' => ['Pacific/Palau'],
'PY' => ['America/Asuncion'],
'QA' => ['Asia/Qatar'],
'RE' => ['Indian/Reunion'],
'RO' => ['Europe/Bucharest'],
'RS' => ['Europe/Belgrade'],
'RU' => ['Asia/Anadyr', 'Asia/Barnaul', 'Asia/Chita', 'Asia/Irkutsk', 'Asia/Kamchatka', 'Asia/Khandyga', 'Asia/Krasnoyarsk', 'Asia/Magadan', 'Asia/Novokuznetsk', 'Asia/Novosibirsk', 'Asia/Omsk', 'Asia/Sakhalin', 'Asia/Srednekolymsk', 'Asia/Tomsk', 'Asia/Ust-Nera', 'Asia/Vladivostok', 'Asia/Yakutsk', 'Asia/Yekaterinburg', 'Europe/Astrakhan', 'Europe/Kaliningrad', 'Europe/Kirov', 'Europe/Moscow', 'Europe/Samara', 'Europe/Saratov', 'Europe/Ulyanovsk', 'Europe/Volgograd', 'W-SU'],
'RW' => ['Africa/Kigali'],
'SA' => ['Asia/Riyadh'],
'SB' => ['Pacific/Guadalcanal'],
'SC' => ['Indian/Mahe'],
'SD' => ['Africa/Khartoum'],
'SE' => ['Europe/Stockholm'],
'SG' => ['Asia/Singapore', 'Singapore'],
'SH' => ['Atlantic/St_Helena'],
'SI' => ['Europe/Ljubljana'],
'SJ' => ['Arctic/Longyearbyen', 'Atlantic/Jan_Mayen'],
'SK' => ['Europe/Bratislava'],
'SL' => ['Africa/Freetown'],
'SM' => ['Europe/San_Marino'],
'SN' => ['Africa/Dakar'],
'SO' => ['Africa/Mogadishu'],
'SR' => ['America/Paramaribo'],
'SS' => ['Africa/Juba'],
'ST' => ['Africa/Sao_Tome'],
'SV' => ['America/El_Salvador'],
'SX' => ['America/Lower_Princes'],
'SY' => ['Asia/Damascus'],
'SZ' => ['Africa/Mbabane'],
'TC' => ['America/Grand_Turk'],
'TD' => ['Africa/Ndjamena'],
'TF' => ['Indian/Kerguelen'],
'TG' => ['Africa/Lome'],
'TH' => ['Asia/Bangkok'],
'TJ' => ['Asia/Dushanbe'],
'TK' => ['Pacific/Fakaofo'],
'TL' => ['Asia/Dili'],
'TM' => ['Asia/Ashgabat', 'Asia/Ashkhabad'],
'TN' => ['Africa/Tunis'],
'TO' => ['Pacific/Tongatapu'],
'TR' => ['Asia/Istanbul', 'Europe/Istanbul', 'Turkey'],
'TT' => ['America/Port_of_Spain'],
'TV' => ['Pacific/Funafuti'],
'TW' => ['Asia/Taipei', 'ROC'],
'TZ' => ['Africa/Dar_es_Salaam'],
'UA' => ['Europe/Kiev', 'Europe/Kyiv', 'Europe/Simferopol', 'Europe/Uzhgorod', 'Europe/Zaporozhye'],
'UG' => ['Africa/Kampala'],
'UM' => ['Pacific/Johnston', 'Pacific/Midway', 'Pacific/Wake'],
'US' => ['America/Adak', 'America/Anchorage', 'America/Atka', 'America/Boise', 'America/Chicago', 'America/Denver', 'America/Detroit', 'America/Fort_Wayne', 'America/Indiana/Indianapolis', 'America/Indiana/Knox', 'America/Indiana/Marengo', 'America/Indiana/Petersburg', 'America/Indiana/Tell_City', 'America/Indiana/Vevay', 'America/Indiana/Vincennes', 'America/Indiana/Winamac', 'America/Indianapolis', 'America/Juneau', 'America/Kentucky/Louisville', 'America/Kentucky/Monticello', 'America/Knox_IN', 'America/Los_Angeles', 'America/Louisville', 'America/Menominee', 'America/Metlakatla', 'America/New_York', 'America/Nome', 'America/North_Dakota/Beulah', 'America/North_Dakota/Center', 'America/North_Dakota/New_Salem', 'America/Phoenix', 'America/Sitka', 'America/Yakutat', 'CST6CDT', 'EST5EDT', 'Navajo', 'PST8PDT', 'US/Alaska', 'US/Aleutian', 'US/Arizona', 'US/Central', 'US/East-Indiana', 'US/Eastern', 'US/Hawaii', 'US/Indiana-Starke', 'US/Michigan', 'US/Mountain', 'US/Pacific'],
'UY' => ['America/Montevideo'],
'UZ' => ['Asia/Samarkand', 'Asia/Tashkent'],
'VA' => ['Europe/Vatican'],
'VC' => ['America/St_Vincent'],
'VE' => ['America/Caracas'],
'VG' => ['America/Tortola', 'America/Virgin'],
'VI' => ['America/St_Thomas'],
'VN' => ['Asia/Ho_Chi_Minh', 'Asia/Saigon'],
'VU' => ['Pacific/Efate'],
'WF' => ['Pacific/Wallis'],
'WS' => ['Pacific/Apia'],
'YE' => ['Asia/Aden'],
'YT' => ['Indian/Mayotte'],
'ZA' => ['Africa/Johannesburg'],
'ZM' => ['Africa/Lusaka'],
'ZW' => ['Africa/Harare'],
];
return isset($countryTimezoneMap[$country_code]) ? array_map('strtolower', $countryTimezoneMap[$country_code]) : [];
}
// 判断字体
private function checkFonts() {
$fonts = !empty($this->fingerprintData['fonts']) ? $this->fingerprintData['fonts'] : [];
if (empty($fonts)) {
return;
}
$os = $this->getOsName();
if ($os === null) {
return;
}
if (preg_match('/Windows/i', $os)) {
// Windows 系统应该有 Calibri 或 Segoe UI,但不应该有 San Francisco
if (!in_array('Calibri', $fonts) && !in_array('Segoe UI', $fonts)) {
$this->riskLevel += 20;
$this->riskFactors[] = "SUSPICIOUS: Windows User-Agent but common Windows fonts like Calibri/Segoe UI are missing.";
}
if (in_array('San Francisco', $fonts)) {
$this->riskLevel += 20;
$this->riskFactors[] = "SUSPICIOUS: Windows User-Agent but macOS-specific font 'San Francisco' was detected.";
}
}
elseif (preg_match('/macOS/i', $os)) {
// macOS 系统应该有 Helvetica Neue 或 San Francisco,但不应该有 Calibri
if (!in_array('Helvetica Neue', $fonts) && !in_array('San Francisco', $fonts)) {
$this->riskLevel += 20;
$this->riskFactors[] = "SUSPICIOUS: macOS User-Agent but common macOS fonts are missing.";
}
if (in_array('Calibri', $fonts)) {
$this->riskLevel += 20;
$this->riskFactors[] = "SUSPICIOUS: macOS User-Agent but Windows-specific font 'Calibri' was detected.";
}
}
}
// 获取风险等级分类
private function getRiskCategory() {
if ($this->riskLevel < 20) {
return '低风险';
} elseif ($this->riskLevel < 50) {
return '中等风险';
} else {
return '高风险';
}
}
// 获取风险等级描述
private function getRiskDescription() {
switch ($this->getRiskCategory()) {
case '低风险':
return '看起来是正常的用户访问,未检测到明显的自动化工具特征';
case '中等风险':
return '检测到一些可能与自动化工具相关的特征,建议进一步观察';
case '高风险':
return '很可能是自动化工具、爬虫或恶意访问,建议采取限制措施';
default:
return '无法确定风险等级';
}
}
}