Google reCAPTCHA Enterprise未拦截Puppeteer自动化请求问题咨询
分析reCAPTCHA Enterprise未拦截Puppeteer脚本的原因及解决方案
核心原因分析
reCAPTCHA Enterprise的评分系统依赖多维度信号(浏览器指纹、交互行为、IP环境、会话模式等)综合判断,你的脚本能拿到0.8高分,大概率是因为没有触发足够的高风险特征:
Puppeteer交互行为接近真人
脚本使用page.type()模拟真实打字输入(默认带按键延迟),而非直接修改DOM值;每次提交后等待页面加载完成,这种操作节奏和真人差异极小,reCAPTCHA难以通过行为模式区分。无头浏览器特征弱化
新版Chrome的无头模式(headless: true)和普通浏览器的指纹差异大幅缩小,没有旧版无头模式特有的机器人标识(比如特殊User-Agent字段),reCAPTCHA的指纹检测模块无法识别。缺少高风险触发信号
脚本的循环操作未触及reCAPTCHA的异常规则:- 没有短时间内高频提交(每次迭代都有页面跳转和等待,间隔足够)
- IP地址未被标记为恶意/代理IP
- 用户名/密码的规律格式未达到reCAPTCHA预设的异常阈值
reCAPTCHA配置未针对性优化
- 可能未针对登录场景设置严格的评分阈值(默认阈值通常为0.5,0.8远高于此)
- 未启用会话跟踪、异常行为检测等高级模块,无法识别同一浏览器的连续登录尝试
解决方案建议
1. 强化reCAPTCHA Enterprise检测能力
- 在Google Cloud控制台的reCAPTCHA Enterprise面板,针对你的site key:
- 调整风险评分阈值:将登录场景的阈值设为0.7或更高,低于该分数的请求直接拦截
- 启用高级检测功能:开启「会话分析」「异常行为检测」,针对同一IP/浏览器的重复登录、规律用户名等行为增加额外风险权重
- 确保前端reCAPTCHA标签的
action参数与后端验证一致(比如登录页设为action="login",后端recaptchaAction也传login),让reCAPTCHA针对场景精准评分
2. 优化后端验证逻辑
- 除了评分,结合
riskAnalysis.reasons判断:如果返回AUTOMATION等机器人相关原因,即使评分较高也拒绝请求 - 补充额外防护规则:比如拦截格式规律的用户名(如
autotester 1这种带序号的格式),限制同一IP的每日登录次数
3. 测试并暴露机器人特征
- 修改脚本,加入明显的机器人行为,观察评分是否下降:
// 直接设置输入框值,而非模拟打字 await page.$eval('input[name="login"]', el => el.value = `autotester ${i}`); await page.$eval('input[name="password"]', el => el.value = 'autotesterpassword'); // 无延迟快速提交 await page.click('button[type="submit"]', { delay: 0 }); - 使用旧版无头模式(
headless: "old"),或修改浏览器指纹(比如禁用WebGL、调整User-Agent),验证reCAPTCHA的检测逻辑是否正常
4. 验证后端代码逻辑
检查createAssessment函数:
- 确认
recaptchaAction参数与前端reCAPTCHA的action完全匹配 - 不要忽略
response.riskAnalysis.reasons中的关键提示,某些场景下即使评分高,也可能存在潜在风险信号
附用户提供的代码
后端reCAPTCHA验证代码
async function createAssessment({ projectID, recaptchaKey, token, recaptchaAction }) { const client = new RecaptchaEnterpriseServiceClient(); const projectPath = client.projectPath(projectID); const target_event = { 'token': token, siteKey: recaptchaKey }; const request = { assessment: { event: target_event }, parent: projectPath, }; console.log('Creating assessment...'); const [response] = await client.createAssessment(request); console.log('Assessment created.'); if (!response.tokenProperties.valid) { console.log(`The CreateAssessment call failed because the token was: ${response.tokenProperties.invalidReason}`); return { score: null, failReason: response.tokenProperties.invalidReason }; } if (response.tokenProperties.action === recaptchaAction) { console.log(`The reCAPTCHA score is: ${response.riskAnalysis.score}`); response.riskAnalysis.reasons.forEach((reason) => { console.log(reason); }); return { score: response.riskAnalysis.score, failReason: null }; } else { console.log("The action attribute in your reCAPTCHA tag does not match the action you are expecting to score"); return { score: null, failReason: "The action attribute in your reCAPTCHA tag does not match the action you are expecting to score" }; } }
Puppeteer自动化脚本代码
const puppeteer = require('puppeteer'); async function run() { const browser = await puppeteer.launch({ headless: true }); const page = await browser.newPage(); for (let i = 1; i <= 50; i++) { try { console.log(`Processing iteration ${i}`); // Navigate to the website console.log(`Iteration ${i}: Navigating to the website`); await page.goto('https://my-domain.com/'); // Enter the username console.log(`Iteration ${i}: Entering the username`); await page.type('input[name="login"]', `autotester ${i}`); // Enter the password console.log(`Iteration ${i}: Entering the password`); await page.type('input[name="password"]', 'autotesterpassword'); // Click the submit button console.log(`Iteration ${i}: Clicking the submit button`); await page.click('button[type="submit"]'); // Wait for the page to load after submitting the form console.log(`Iteration ${i}: Waiting for the page to load`); await page.waitForSelector('#loadingDialog', { hidden: true }); // Take a screenshot or perform further actions if needed console.log(`Iteration ${i}: Taking a screenshot`); await page.screenshot({ path: `screenshot_${i}.png` }); console.log(`Iteration ${i} completed successfully`); } catch (error) { console.error(`Error occurred in iteration ${i}:`, error); } } // Close the browser await browser.close();}run().catch(error => console.error(error));
内容的提问来源于stack exchange,提问作者nullmicgo
相关产品推荐
相关产品推荐

