CAPSOLVER
博客
如何用NodeJS解决AWS验证码

如何使用NodeJS解决AWS验证码

Logo of CapSolver

Ethan Collins

Pattern Recognition Specialist

03-Nov-2025

在现代的网络环境中,保护在线资源免受自动化威胁至关重要。AWS Web Application Firewall (WAF) 是一个强大的安全层,通常会部署挑战或托管验证码来过滤恶意机器人。虽然对安全有效,但这些机制对合法的自动化任务(如网页爬取、监控或测试)构成了重大障碍。

本全面指南使用 Node.jsCapSolver 服务提供了一个强大的自动化解决方案。我们将逐步介绍如何设置环境、实现模块化的 Node.js 脚本,并利用 CapSolver 的专用 AntiAwsWafTask 来绕过 WAF 挑战(HTTP 202)和 WAF 验证码(HTTP 405)场景。在本教程结束时,您将拥有一个可直接投入生产的脚本,能够获取访问受保护内容所需的 aws-waf-token cookie。

⚙️ 前提条件

在开始之前,请确保准备好以下环境和信息:

  1. Node.js 环境:您的系统上已安装 Node.js(推荐使用 LTS 版本)。
  2. CapSolver API 密钥:您需要一个 CapSolver 账户和您的 API 密钥。
  3. 代理(可选):如果目标网站有地理限制或您需要隐藏真实 IP,请准备一个 HTTP/HTTPS 代理。

🛠️ 第一步:安装必要的依赖

在您的项目目录中,执行以下命令以安装所需的 Node.js 模块:

bash 复制代码
npm install axios cheerio
  • axios:用于发送 HTTP 请求。
  • cheerio:用于解析 HTML 内容并提取 AWS WAF 挑战所需的参数。

💻 第二步:Node.js 核心代码实现

以下是用于解决 AWS WAF 挑战和验证码的 Node.js 脚本。它会自动检测网站返回的状态码,并根据需要执行相应的 CapSolver 任务。

请将以下代码保存为 aws_waf_solver.js

javascript 复制代码
const axios = require('axios');
const cheerio = require('cheerio');
const { URL } = require('url');

// ⚠️ 配置:请替换为您的实际值
const CLIENT_KEY = "YOUR_CAPSOLVER_API_KEY"; // 替换为您的 CapSolver API 密钥
const PAGE_URL = "https://norway-meetup.aws.wslab.no/"; // 替换为目标网站 URL
const PROXY = "YOUR_PROXY_ADDRESS"; // 替换为您的代理地址(格式:user:pass@ip:port 或 ip:port)

// --- 辅助函数 ---

/**
 * 暂停执行指定的毫秒数
 * @param {number} ms 毫秒数
 */
function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

/**
 * 创建 CapSolver 任务
 * @param {object} payload 任务负载
 * @returns {Promise<object>} 任务创建结果
 */
async function createTask(payload) {
    try {
        const res = await axios.post('https://api.capsolver.com/createTask', {
            clientKey: CLIENT_KEY,
            task: payload
        });
        if (res.data.errorId !== 0) {
            throw new Error(`CapSolver API 错误: ${res.data.errorDescription}`);
        }
        return res.data;
    } catch (error) {
        console.error("创建 CapSolver 任务失败:", error.message);
        return null;
    }
}

/**
 * 获取 CapSolver 任务结果,直到任务完成
 * @param {string} taskId 任务 ID
 * @returns {Promise<object>} 任务结果
 */
async function getTaskResult(taskId) {
    if (!taskId) return null;
    console.log(`等待任务结果(ID: ${taskId})...`);
    try {
        let success = false;
        let result = null;
        while (!success) {
            await sleep(3000); // 每 3 秒查询一次
            const res = await axios.post('https://api.capsolver.com/getTaskResult', {
                clientKey: CLIENT_KEY,
                taskId: taskId
            });

            if (res.data.errorId !== 0) {
                throw new Error(`CapSolver API 错误: ${res.data.errorDescription}`);
            }

            if (res.data.status === "ready") {
                success = true;
                result = res.data;
                console.log("任务完成,已获得解决方案。");
            } else if (res.data.status === "processing") {
                console.log("任务仍在处理中...");
            }
        }
        return result;
    } catch (error) {
        console.error("获取 CapSolver 任务结果失败:", error.message);
        return null;
    }
}

// --- 核心求解函数 ---

/**
 * 解决 AWS WAF 挑战(状态码 202)
 * @param {string} awsChallengeJS AWS 挑战 JavaScript URL
 * @returns {Promise<string|null>} 解决后的 AWS WAF Cookie 值
 */
async function solveAwsChallenge(awsChallengeJS) {
    console.log("检测到 AWS WAF 挑战(状态码 202),开始求解...");
    const taskPayload = {
        type: "AntiAwsWafTask",
        websiteURL: PAGE_URL,
        awsChallengeJS,
        proxy: PROXY
    };
    const taskData = await createTask(taskPayload);
    if (!taskData) return null;

    const result = await getTaskResult(taskData.taskId);
    if (result && result.solution && result.solution.cookie) {
        return result.solution.cookie;
    }
    return null;
}

/**
 * 解决 AWS WAF 验证码 + 挑战(状态码 405)
 * @param {string} htmlContent 包含验证码参数的 HTML 内容
 * @param {string} awsChallengeJS AWS 挑战 JavaScript URL
 * @returns {Promise<string|null>} 解决后的 AWS WAF Cookie 值
 */
async function solveAwsCaptchaChallenge(htmlContent, awsChallengeJS) {
    console.log("检测到 AWS WAF 验证码(状态码 405),开始求解...");
    const $ = cheerio.load(htmlContent);
    const scriptContent = $("script[type='text/javascript']").last().html();

    if (!scriptContent) {
        console.error("无法找到包含验证码参数的脚本内容。");
        return null;
    }

    // 使用正则表达式提取关键参数
    const keyMatch = /"key":"(.*?)"/.exec(scriptContent);
    const ivMatch = /"iv":"(.*?)"/.exec(scriptContent);
    const contextMatch = /"context":"(.*?)"/.exec(scriptContent);

    const key = keyMatch ? keyMatch[1] : null;
    const iv = ivMatch ? ivMatch[1] : null;
    const context = contextMatch ? contextMatch[1] : null;

    if (!key || !iv || !context) {
        console.error("从脚本中未能提取所有必需的验证码参数(key, iv, context)。");
        return null;
    }

    console.log(`提取的参数: Key=${key}, IV=${iv}, Context=${context}`);

    const taskPayload = {
        type: "AntiAwsWafTask", // CapSolver 统一使用此任务类型
        websiteURL: PAGE_URL,
        awsKey: key,
        awsIv: iv,
        awsContext: context,
        awsChallengeJS,
        proxy: PROXY
    };

    const taskData = await createTask(taskPayload);
    if (!taskData) return null;

    const result = await getTaskResult(taskData.taskId);
    if (result && result.solution && result.solution.cookie) {
        return result.solution.cookie;
    }
    return null;
}

/**
 * 主执行函数
 */
async function main() {
    let awsWafCookie = null;
    let initialResponse = null;

    // 1. 初始请求目标页面
    try {
        console.log(`请求目标页面: ${PAGE_URL}`);
        initialResponse = await axios.get(PAGE_URL, {
            headers: {
                // 模拟浏览器请求头
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "Accept-Encoding": "gzip, deflate, br",
                "Accept-Language": "en-US,en;q=0.9",
                "Cache-Control": "max-age=0",
                "Upgrade-Insecure-Requests": "1"
            },
            // 允许处理 2xx、3xx、4xx 状态码
            validateStatus: (status) => status >= 200 && status < 500
        });
        console.log(`初始响应状态码: ${initialResponse.status}`);
    } catch (error) {
        console.error(`初始请求失败: ${error.message}`);
        return;
    }

    const $ = cheerio.load(initialResponse.data);
    const scriptTags = $('script[src*="token.awswaf.com"]');
    const awsChallengeJS = scriptTags.attr('src');

    if (!awsChallengeJS) {
        console.log("未检测到 AWS WAF 挑战脚本。该网站可能未受保护或已通过验证。");
        // 如果没有挑战脚本,直接使用初始响应
        if (initialResponse.status === 200) {
            console.log("网站加载成功。");
            // console.log(initialResponse.data); // 打印最终内容
            return;
        }
    } else {
        console.log(`检测到 AWS WAF 挑战脚本 URL: ${awsChallengeJS}`);
    }


    // 2. 根据状态码确定并解决挑战/验证码
    if (initialResponse.status === 202) {
        // 仅 AWS WAF 挑战
        awsWafCookie = await solveAwsChallenge(awsChallengeJS);
    } else if (initialResponse.status === 405) {
        // AWS WAF 验证码 + 挑战
        awsWafCookie = await solveAwsCaptchaChallenge(initialResponse.data, awsChallengeJS);
    } else if (initialResponse.status === 200) {
        console.log("网站加载成功,无需验证码求解。");
        // console.log(initialResponse.data); // 打印最终内容
        return;
    } else {
        console.log(`遇到未处理的状态码: ${initialResponse.status}。`);
        return;
    }

    // 3. 使用获取的 Cookie 再次请求目标页面
    if (awsWafCookie) {
        try {
            console.log("\n--- 第二次请求:使用 AWS WAF Cookie ---");
            console.log(`使用的 Cookie: ${awsWafCookie}`);

            const finalResponse = await axios.get(PAGE_URL, {
                headers: {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
                    "Cookie": `aws-waf-token=${awsWafCookie}`
                },
                validateStatus: (status) => status === 200 // 期望最终成功
            });

            console.log(`最终响应状态码: ${finalResponse.status}`);
            console.log("成功获取网站内容!");
            // console.log(finalResponse.data); // 打印最终内容
        } catch (error) {
            console.error(`最终请求失败: ${error.message}`);
        }
    } else {
        console.log("未能获取 AWS WAF Cookie,无法执行第二次请求。");
    }
}

main();

💡 关键点总结

关键点 原始代码问题 优化改进
依赖安装 缺少 cheerio,使用了错误的 Python 语法高亮。 明确说明需要安装 axioscheerio,使用正确的 bash 语法高亮。
任务类型 混合使用 FunCaptchaTaskProxylessAntiAwsWafTask 统一使用 CapSolver 官方推荐的 AntiAwsWafTask 处理所有 AWS WAF 场景。
参数提取 405 逻辑中提取 awsChallengeJS 的逻辑不完整,202 逻辑中未提取。 main 函数中统一提取 awsChallengeJS,并根据状态码调用不同的求解函数(202 或 405)。
错误处理 简单的错误处理,缺乏 API 错误检查和任务等待提示。 createTaskgetTaskResult 中添加 CapSolver API 错误检查(errorId !== 0),并在任务处理期间提供状态提示。
代码结构 所有逻辑集中在 main 函数中,可读性差。 分离为清晰的辅助函数(sleepcreateTaskgetTaskResult)和核心求解函数(solveAwsChallengesolveAwsCaptchaChallenge),使逻辑更清晰。
请求头 原始请求头冗余且不完整。 简化并使用更标准的浏览器模拟请求头。

❓ 常见问题(FAQ)

问:为什么需要处理 HTTP 202 和 HTTP 405 状态码?

答: AWS WAF 使用不同的安全措施,导致不同的 HTTP 状态码:

  • HTTP 202(已接受):通常表示需要 WAF 挑战。这是一种较轻量的安全检查,通常涉及运行 JavaScript 代码。
  • HTTP 405(方法不允许):通常表示需要 WAF 验证码,这是一种更复杂的检查,涉及解决视觉或交互式谜题,同时需要挑战参数。我们的脚本设计为能自动检测并处理这两种情况。

问:可以不使用代理使用此脚本吗?

答: 可以。PROXY 变量是可选的。如果您不需要代理,可以在配置中将 const PROXY = "";。然而,对于网页爬取和自动化任务,使用高质量的代理是非常推荐的,以避免 IP 被封禁并确保稳定的访问。

问:什么是 AntiAwsWafTask

答: AntiAwsWafTask 是 CapSolver 提供的一种专用任务类型,专门用于处理 AWS WAF 安全机制。它智能地处理从受保护页面提取的挑战参数(包括 awsKeyawsIvawsContextawsChallengeJS),并返回绕过保护所需的 aws-waf-token cookie。

📚 更多信息

✅ 结论

本指南展示了使用Node.js和**CapSolver**编程解决AWS WAF挑战和验证码的稳健且高效方法。通过实现模块化脚本并利用CapSolver的专业任务类型,您可以将此解决方案无缝集成到自动化工作流中。成功的关键在于正确识别WAF状态码(202或405),提取必要的参数,并将生成的aws-waf-token cookie用于后续请求。这种方法确保您的自动化任务可以可靠地访问受AWS WAF保护的内容。

合规声明: 本博客提供的信息仅供参考。CapSolver 致力于遵守所有适用的法律和法规。严禁以非法、欺诈或滥用活动使用 CapSolver 网络,任何此类行为将受到调查。我们的验证码解决方案在确保 100% 合规的同时,帮助解决公共数据爬取过程中的验证码难题。我们鼓励负责任地使用我们的服务。如需更多信息,请访问我们的服务条款和隐私政策。

更多