reCaptcha V2运行正常却无法拦截垃圾信息 代码问题排查求助
代码问题分析
1. 未校验Google返回结果的hostname字段
Google的siteverify接口返回结果中除了success字段,还有hostname字段,代表验证操作实际是在哪个域名下完成的。你的代码只校验success是否为真,没有校验hostname是否为自身站点域名。攻击者可以通过打码服务拿到同一个site key生成的有效验证响应,甚至直接复用其他同site key站点生成的有效响应,只要响应本身合法,Google就会返回success=true,直接绕过校验。
2. 验证请求未传递用户IP参数remoteip
reCaptcha验证接口支持传递remoteip参数,值为当前访问用户的真实IP。你没有传递该参数的情况下,Google不会校验验证操作的发起IP和表单提交IP是否一致,攻击者可以在自己的设备上完成验证拿到有效响应后,用服务器批量提交表单,只要响应未过期就能通过校验。
3. 未对Google接口请求结果做合法性校验
你直接使用file_get_contents的返回结果解码,没有判断请求是否成功:
- 当服务器网络波动、无法连接Google接口时,
file_get_contents会返回false,json_decode(false)得到null,此时访问$verify_response->success会抛出PHP Notice错误,如果站点关闭了错误显示,部分旧版本PHP的错误处理逻辑会将该表达式判定为true,直接进入提交成功分支 - 你直接把用户提交的
g-recaptcha-response拼接到请求URL中,没有做URL编码,如果响应值中包含&、=等特殊字符,会导致请求参数被篡改,极端情况下可被利用构造出返回success=true的请求
4. 缺少额外的垃圾提交拦截逻辑
reCaptcha V2本身可被打码服务、AI自动识别工具绕过,只靠验证码校验无法100%拦截垃圾提交,可补充以下规则:
- 提交频率限制:同一IP/同一设备短时间内多次提交直接拦截
- 蜜罐字段校验:添加正常用户看不到的隐藏表单字段,只要该字段有值就判定为爬虫拦截
- 内容敏感词过滤:匹配赌博、违规内容关键词直接拦截
修复代码示例
if(isset($_POST['submit'])){ if(isset($_POST['g-recaptcha-response']) && !empty($_POST['g-recaptcha-response'])){ // 编码用户提交的响应,避免参数篡改 $response = urlencode($_POST['g-recaptcha-response']); // 获取用户真实IP(有反向代理的场景需要调整获取IP的逻辑) $remoteip = $_SERVER['REMOTE_ADDR']; // 拼接请求URL,新增remoteip参数 $verify_url = 'https://www.google.com/recaptcha/api/siteverify?secret='.$secret_key.'&response='.$response.'&remoteip='.$remoteip; $verify_captcha = file_get_contents($verify_url); // 判断接口请求是否成功 if($verify_captcha === false){ $returnMsg = '验证码验证服务暂时不可用,请稍后重试'; include ('./forms/sectionhead.tpl'); echo '<p class="returnmsg">reCAPTCHA error: ' . $returnMsg . '</p>'; include ('./forms/contactform.php'); include ('./forms/contactend.php'); exit(); } $verify_response = json_decode($verify_captcha); // 判断JSON解码是否成功,返回结果是否为合法对象 if($verify_response === null || !is_object($verify_response)){ $returnMsg = '验证码验证失败,请重试'; include ('./forms/sectionhead.tpl'); echo '<p class="returnmsg">reCAPTCHA error: ' . $returnMsg . '</p>'; include ('./forms/contactform.php'); include ('./forms/contactend.php'); exit(); } // 新增hostname校验,替换为你自己的站点域名 if($verify_response->success && isset($verify_response->hostname) && $verify_response->hostname === 'yourdomain.com'){ $returnMsg = 'Your email has been submitted successfully.'; include ('./includes/sendmail.php'); include ('./forms/sectionhead.tpl'); // 输出用户提交内容前做HTML转义,避免XSS漏洞 $safe_email = htmlspecialchars($_POST['form_email'], ENT_QUOTES); echo "<p class='returnmsg'>reCAPTCHA was completed successfully!<br> $returnMsg</p>"; echo " <div class=\"bg-theme-colored\" style=\"color:#754c00;border:1px solid #11477f;text-align:center;padding:25px;\"> <h4><span>Success!</span> Your message has been sent.</h4> <h5>A copy of your message has been emailed to $safe_email</h5> <h5>Thank you for your interest in the North American Bison </h5> </div>"; include ('./forms/contactend.php'); exit(); }else{ $returnMsg = 'reCaptch verification failed, please verify again.'; include ('./forms/sectionhead.tpl'); echo '<p class="returnmsg">reCAPTCHA error: ' . $returnMsg . '</p>'; include ('./forms/contactform.php'); include ('./forms/contactend.php'); exit(); } }else{ $returnMsg = 'Please check the CAPTCHA box.'; include ('./forms/sectionhead.tpl'); echo '<p class="returnmsg">reCAPTCHA error: ' . $returnMsg . '</p>'; include ('./forms/contactform.php'); include ('./forms/contactend.php'); exit(); } }
内容的提问来源于stack exchange,提问作者Rod
相关产品推荐
相关产品推荐

