You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JS实现带验证码的表单安全提交?

表单验证码绕过问题的解决方案

核心原则:后端必须强制校验验证码

前端任何限制都能通过浏览器控制台绕过,所以后端校验是唯一可靠的安全防线:

  • 不要在前端生成验证码,改为后端生成并存储(比如存在Symfony的Session、Redis或带过期时间的缓存中)。用户请求发送验证码时,后端生成验证码、存储、同时触发邮件发送,再把验证码相关标识返回给前端(不要直接返回验证码内容)。
  • 在处理表单提交的Symfony控制器中,先取出用户输入的验证码,和服务器存储的对应值做对比:不匹配则直接返回错误,拒绝处理表单;匹配成功后再执行后续逻辑,同时销毁该验证码(防止重复使用)。

前端辅助限制(提升体验+增加攻击成本)

虽然前端限制不能替代后端校验,但可以优化正常用户体验,同时增加攻击者的操作成本:

  • 替换原生提交按钮为普通按钮,点击后先完成验证码验证流程,验证通过后再调用form.submit():
document.getElementById('verify-btn').addEventListener('click', async function() {
  const code = document.getElementById('verify_code').value;
  // 调用后端接口校验验证码
  const res = await fetch('/api/verify-code', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body: `code=${encodeURIComponent(code)}`
  });
  const data = await res.json();
  if (data.success) {
    // 验证通过,提交表单
    document.getElementById('sharing_form').submit();
  } else {
    alert('验证码错误,请重新输入');
  }
});
  • 给表单添加onsubmit事件拦截,只有验证通过后才允许提交:
let isCodeVerified = false;

// 验证码验证逻辑
async function checkCode(code) {
  const res = await fetch('/api/verify-code', { /* 配置同上 */ });
  const data = await res.json();
  isCodeVerified = data.success;
  return isCodeVerified;
}

// 拦截表单原生提交
document.getElementById('sharing_form').addEventListener('submit', function(e) {
  if (!isCodeVerified) {
    e.preventDefault();
    alert('请先完成验证码验证');
  }
});

进阶替代方案:验证后发放一次性令牌

如果不想在表单提交时重复校验验证码,可以采用“验证后发令牌”的方式:

  1. 用户输入验证码并提交验证,后端校验通过后,生成一个一次性、带过期时间的令牌,存储到服务器并返回给前端。
  2. 前端将令牌作为隐藏字段插入表单(比如<input type="hidden" name="verify_token" value="xxx">)。
  3. 后端处理表单提交时,先校验这个令牌的有效性:令牌不存在、已过期或不匹配,则直接拒绝提交;校验通过后再处理表单逻辑,同时销毁令牌。

这种方式下,即使用户直接调用form.submit(),因为没有有效令牌,后端也会拒绝请求,从根源上绕过了前端提交的限制。

内容的提问来源于stack exchange,提问作者TugBenson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 02:40:31