
本文详解如何在基于 phpmailer 6 的多附件联系表单中,可靠实现 google recaptcha v2 服务端验证,避免验证码被忽略或表单无校验提交的问题。
本文详解如何在基于 phpmailer 6 的多附件联系表单中,可靠实现 google recaptcha v2 服务端验证,避免验证码被忽略或表单无校验提交的问题。
在实际开发中,许多开发者成功集成了 PHPMailer 发送邮件及多文件上传功能,却常因遗漏关键验证环节而导致 reCAPTCHA v2 形同虚设——表单无论是否勾选验证码均能提交。根本原因在于:前端渲染的 reCAPTCHA 组件仅生成 token,真正的验证必须由后端主动调用 Google 的 siteverify API 完成,且该逻辑必须置于邮件发送流程之前,并严格控制执行路径。
以下是完整、可落地的解决方案:
✅ 正确的 reCAPTCHA v2 集成流程
-
前端确保 token 正确提交
您的 HTML 中使用正确,但需确认:- data-sitekey 值为 Google Cloud Console 中为您的域名注册的 Site Key(非 Secret Key);
- 表单 method="post" 且未被 JavaScript 意外拦截(如 event.preventDefault() 后未手动提交);
- 提交时,Google 自动将一次性 token 注入 $_POST['g-recaptcha-response'] —— 这是后端验证的唯一依据。
后端必须进行强制服务端验证
您原代码中存在两个关键缺陷:- if(isset($_POST['submit'], $_POST['g-recaptcha-response'])) 条件块未包裹全部业务逻辑,导致即使验证失败,后续邮件代码仍可能执行;
- 变量名笔误:$captchResult(少一个 a)导致条件判断恒为 false,无法进入邮件发送分支。
✅ 正确写法如下(已修复逻辑与命名):
<?php use PHPMailer\PHPMailer\PHPMailer; use PHPMailer\PHPMailer\Exception; require './src/Exception.php'; require './src/PHPMailer.php'; require './src/SMTP.php'; class ReCaptchaValidator { private $secretKey = 'YOUR_SECRET_KEY_HERE'; // 替换为 Google 控制台获取的 Secret Key public function verify($response): bool { if (empty($response)) { return false; } $url = 'https://www.google.com/recaptcha/api/siteverify'; $data = [ 'secret' => $this->secretKey, 'response' => $response, 'remoteip' => $_SERVER['REMOTE_ADDR'] ?? '' ]; $options = [ 'http' => [ 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data) ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); if ($result === false) { error_log('reCAPTCHA verification request failed'); return false; } $responseJson = json_decode($result, true); return $responseJson['success'] ?? false; } } // ⚠️ 关键:所有业务逻辑必须在此验证通过后执行 if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit'])) { $recaptchaResponse = $_POST['g-recaptcha-response'] ?? ''; $recaptcha = new ReCaptchaValidator(); if (!$recaptcha->verify($recaptchaResponse)) { die('❌ reCAPTCHA 验证失败,请重试。'); } // ✅ 验证通过,继续处理邮件 try { $mail = new PHPMailer(true); $mail->isSMTP(); $mail->Host = 'in-v3.mailjet.com'; $mail->SMTPAuth = true; $mail->Username = 'YOUR_MAILJET_API_KEY'; $mail->Password = 'YOUR_MAILJET_API_SECRET'; $mail->SMTPSecure = 'tls'; $mail->Port = 587; $mail->setFrom($_POST['email'] ?? 'no-reply@example.com', $_POST['name'] ?? 'Anonymous'); $mail->addAddress('your-email@example.com'); // 收件人 $mail->addReplyTo($_POST['email'], $_POST['name']); // ✅ 安全处理附件:使用 $_FILES['attachment'] 而非硬编码 'userfile' if (!empty($_FILES['attachment']['name'][0])) { foreach ($_FILES['attachment']['tmp_name'] as $key => $tmpName) { if (is_uploaded_file($tmpName) && $_FILES['attachment']['error'][$key] === UPLOAD_ERR_OK) { $mail->addAttachment($tmpName, $_FILES['attachment']['name'][$key]); } } } $mail->isHTML(true); $mail->Subject = '网站咨询表单提交'; $mail->Body = <strong>发件人:</strong>{$_POST['name'] ?? 'N/A'} <p><strong>公司:</strong>{$_POST['company'] ?? 'N/A'}</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2138" title="PHP"><img src="https://img.php.cn/upload/skill/000/000/081/178884013267959.jpg" alt="PHP" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill2138" title="PHP" class="overflowclass">PHP</a> <p class="overflowclass">编写健壮的PHP代码,规避类型转换陷阱、数组怪癖及常见安全漏洞。</p> </div> <a rel="nofollow" href="/xiazai/skill2138" title="PHP" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div> <p><strong>电话:</strong>{$_POST['phone'] ?? 'N/A'}</p> <p><strong>邮箱:</strong>{$_POST['email'] ?? 'N/A'}</p> <p><strong>留言:</strong><br>{nl2br(htmlspecialchars($_POST['message'] ?? ''))}</p> HTML; $mail->send(); header('Location: thank.html'); exit; } catch (Exception $e) { http_response_code(500); echo "邮件发送失败:{$mail->ErrorInfo}"; } } else { http_response_code(400); echo '❌ 无效请求:请通过表单提交。'; } ?>⚠️ 关键注意事项
- Secret Key 保密性:$secretKey 必须严格保密,绝不可暴露在前端或公开仓库中;建议通过环境变量加载(如 getenv('RECAPTCHA_SECRET'))。
- 附件字段名一致性:HTML 中 对应 PHP 中 $_FILES['attachment'],原代码误用 'userfile' 导致附件丢失。
- 错误处理增强:使用 file_get_contents() 替代 cURL 可减少依赖,但需确保 allow_url_fopen=On;若禁用,可回退至 cURL(注意启用 CURLOPT_SSL_VERIFYPEER=false 仅用于测试,生产环境应配置 CA 证书)。
- 用户体验优化:前端可添加简单 JS 校验(如检查 grecaptcha.getResponse() 是否为空),但不能替代服务端验证——它仅作辅助,防止明显恶意绕过。
- 调试技巧:临时开启 error_log(print_r($_POST, true)); 查看是否收到 g-recaptcha-response;用 var_dump($responseJson) 检查 Google 返回结构(含 error-codes 字段,如 invalid-input-secret 表示密钥错误)。
通过以上重构,reCAPTCHA v2 将真正成为表单提交的第一道安全闸门,有效抵御自动化垃圾邮件攻击,同时保持 PHPMailer 多附件功能完整可用。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










