新会话场景下基于客户端数据生成指纹时的wp_safe_redirect延迟执行问题求助
我写了一个自定义脚本,会结合服务端和客户端数据生成指纹,用来阻止那些换不同IP反复访问的用户。我知道这不是100%可靠,但作为最后的安全机制,目前运行得还不错。
我遇到的问题是,当新会话首次加载页面时,PHP代码会比JavaScript收集客户端数据的操作先执行。具体来说,指纹函数被调用时,会话里还没有指纹,所以触发了generate_fingerprint(),但此时客户端数据还没通过POST传过来(因为JS还没运行),导致这个函数返回null,进而让阻塞机制在这个场景下失效。
以下是我用来生成基于客户端数据的指纹的代码(配合一个JavaScript文件):
function generate_advanced_fingerprint() { $components = [ 'screen_resolution' => $_POST['screen_resolution'] ?? '', 'color_depth' => $_POST['color_depth'] ?? '', 'canvas_fingerprint' => $_POST['canvas_fingerprint'] ?? '', 'webgl_fingerprint' => $_POST['webgl_fingerprint'] ?? '', 'audio_fingerprint' => $_POST['audio_fingerprint'] ?? '', 'installed_fonts' => $_POST['installed_fonts'] ?? '', 'hardware_concurrency' => $_POST['hardware_concurrency'] ?? '', 'device_memory' => $_POST['device_memory'] ?? '', 'browser_features' => $_POST['browser_features'] ?? '' ]; if (in_array('', $components, true)) { return null; } ksort($components); $structured_data = json_encode($components); $fingerprint = md5($structured_data); return $fingerprint; }
注:JavaScript是在<head>标签顶部执行的。
下面是我用来生成指纹并对比黑名单(一个JSON文件)进行阻塞检查的代码:
function handle_fingerprint() { if (!is_page('test')) { return; } $is_post_request = ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_FINGERPRINT_REQUEST'])); if ($is_post_request) { // Process the POST data with client-side information $raw_post_data = file_get_contents('php://input'); $json_data = json_decode($raw_post_data, true); if ($json_data) { $_POST = array_merge($_POST, $json_data); } } // Generate or retrieve the fingerprint if (!isset($_SESSION['fingerprint_generated']) || $is_post_request) { $fingerprint = generate_fingerprint(); $_SESSION['fingerprint_generated'] = true; $_SESSION['current_fingerprint'] = $fingerprint; // Save the fingerprint if it is a POST request if ($is_post_request) { $ip = get_visitor_ip(); save_fingerprint($ip, $fingerprint); // Send a response to the POST request header('Content-Type: application/json'); echo json_encode(['success' => true]); exit; } } else { $fingerprint = $_SESSION['current_fingerprint']; } // Run the check $blocked_fingerprints = get_blocked_fingerprints(); // Check fallback fingerprint if (in_array($fingerprint['fallback_fingerprint'], $blocked_fingerprints)) { wp_safe_redirect(home_url('/')); exit; } // Check advanced fingerprint if (isset($fingerprint['advanced_fingerprint']) && in_array($fingerprint['advanced_fingerprint'], $blocked_fingerprints)) { wp_safe_redirect(home_url('/')); exit; } } // Check the fingerprint on every pageload add_action('wp', 'handle_fingerprint'); // Process POST requests for fingerprint data add_action('init', function() { if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER['HTTP_X_FINGERPRINT_REQUEST'])) { handle_fingerprint(); } });
在新会话这个场景下,页面加载完成后,JavaScript会执行window.getFingerprint(),这个函数会收集客户端数据并通过POST请求发送到服务器,再次触发handle_fingerprint()。这时POST数据已经可用,generate_advanced_fingerprint()就能成功执行,生成完整的指纹。但这会导致两次调用(我可以通过错误日志看到)。
我试过只在所有数据都到位时才生成指纹:
if ($all_data_present) { if (!isset($_SESSION['fingerprint_generated'])) { $fingerprint = generate_fingerprint(); $_SESSION['fingerprint_generated'] = true; $_SESSION['current_fingerprint'] = $fingerprint; } else { $fingerprint = $_SESSION['current_fingerprint']; }
这个方法能生成指纹,但问题是当客户端数据到位时,页面内容已经输出了,wp_safe_redirect()的跳转机制就失效了。现在我想找一种方法,能延迟阻塞检查直到客户端数据可用,但至今还没成功。
另外还有两个要求:
- 我不想显示错误消息,宁愿让用户跳转到一个“伪装”页面来迷惑他们,不让他们察觉到异常
- 我也不想用JavaScript来做跳转,希望尽量使用
wp_safe_redirect()这类服务端方法
有没有人能从新的角度帮我看看这个问题?
备注:内容来源于stack exchange,提问作者Flower88

