如果你 cloudflare 人机验证总是失败

· · 科技·工程

在我的网络环境下(山东济南移动),加载 cloudflare 人机验证常常需要验证很久然后失败.

在网络请求中可以看到失败的请求用时总是 5s、10s 与 15s,非常准时间,看来是网络请求在客户端一侧被掐断了.打断点进去看到请求用的是 XMLHttpRequest,设置了 timeout 参数,若加载过慢会自动掐断.将 timeout 取消就可以解决问题.

于是我让 AI 写了个油猴脚本.安装后 cloudflare 小组件的所有请求都不会有 timeout.等待约 30s 就可以通过.

我不知道其他人是什么情况,反正 it works on my machine.

// ==UserScript==
// @name         Choudflare 人机验证辅助脚本
// @namespace    https://mrpython.top/
// @version      1.0
// @description  劫持 XMLHttpRequest,在 send 时对特定链接设置 timeout 为 undefined
// @match        https://challenges.cloudflare.com/cdn-cgi/challenge-platform/*
// @run-at       document-start
// @grant        unsafeWindow
// ==/UserScript==

(function() {
    'use strict';

    // ================= 配置区 =================
    // 设置你需要拦截的特定链接特征(可以是完整 URL,也可以是部分路径关键字)
    const TARGET_URL_FEATURE = '';
    // ==========================================

    // 获取网页真实的 window 对象,防止沙盒隔离
    const win = unsafeWindow || window;
    const XHR = win.XMLHttpRequest;

    if (!XHR) {
        console.warn('[XHR Hook] 当前环境不支持 XMLHttpRequest');
        return;
    }

    // 保存原生方法的引用,防止被覆盖后丢失
    const originalOpen = XHR.prototype.open;
    const originalSend = XHR.prototype.send;

    /**
     * 1. 劫持 open 方法
     * 目的:在请求初始化时,捕获并记录请求的 URL 地址
     */
    XHR.prototype.open = function(method, url, async, user, password) {
        // 将 url 挂载到当前 XHR 实例的自定义属性上
        // 使用 String() 是为了兼容某些库传入 URL 对象的情况
        this._hook_url = url ? String(url) : '';

        // 透传所有参数,调用原生 open 方法
        return originalOpen.apply(this, arguments);
    };

    /**
     * 2. 劫持 send 方法
     * 目的:在请求真正发送前,检查 URL 并修改 timeout 属性
     */
    XHR.prototype.send = function(body) {
        const url = this._hook_url;

        // 判断当前请求是否匹配目标链接
        if (url && url.includes(TARGET_URL_FEATURE)) {
            console.log(`%c[XHR Hook] 拦截到目标请求: ${url}`, 'color: #00ff00; font-weight: bold;');
            console.log(`[XHR Hook] 修改前 timeout: ${this.timeout}`);

            // 按照你的要求,将 timeout 设置为 undefined
            this.timeout = undefined;

            console.log(`[XHR Hook] 修改后 timeout: ${this.timeout}`);
        }

        // 透传参数,调用原生 send 方法真正发送请求
        return originalSend.apply(this, arguments);
    };

    console.log('%c[XHR Hook] XMLHttpRequest 劫持已就绪!', 'color: #0088ff; font-weight: bold;');
})();