必须手动注入CSRF Token,因Layui不自动识别meta或外部token;需在form.on('submit')中通过DOM操作写入hidden字段或拼入提交数据,确保后端能读取。

layui 表单提交前必须手动注入 CSRF Token
layui 本身不自动识别或追加 __RequestVerificationToken 或 csrf_token 字段,所有 token 都得你亲手塞进去。它只管渲染和收集表单 DOM 值,对安全字段完全无感。
常见错误是以为调了 layui.form.render() 就能“激活” token,结果提交时根本没发出去——因为 token 根本不在表单 DOM 里。
- 在 HTML 模板中直接写:
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">(服务端模板如 Jinja2/Thymeleaf) - 若用纯静态 HTML + AJAX 提交,需在 JS 中提前读取:
document.querySelector('meta[name="csrf-token"]')?.getAttribute('content'),再手动拼进数据对象 - 不要依赖
layui.form.serialize()自动包含 token:它只扫<form></form>内部的可序列化控件,<meta>或外部变量一律忽略
form.on('submit') 里改 DOM 才算真正生效
很多人用 form.val('filter', {csrf_token: 'xxx'}) 赋值,但提交时后端收不到——因为 form.val 只更新 layui 的内部缓存,不写回原生 <input> 的 value 属性。
真正起作用的是浏览器原生提交时读取的 DOM 值,不是 layui 缓存。
- 拦截提交:
form.on('submit(*)', function(data){ e.preventDefault(); }) - 直接操作 DOM:
document.querySelector('input[name="csrf_token"]').value = tokenValue - 如果是动态生成的 token(比如从 API 获取),必须在 submit 回调里赋值,不能提前写死在 HTML 里
- 修改完记得检查是否真写入了:
console.log(document.querySelector('input[name="csrf_token"]').value)
AJAX 提交时 token 必须进 data 对象,不能只靠 headers
后端校验逻辑往往只认 form-data 或 JSON body 里的 csrf_token 字段,比如 Flask 的 CSRFProtect、ASP.NET 的 [ValidateAntiForgeryToken] 默认都不看请求头。
把 token 放进 headers: {'X-CSRF-Token': token} 看似干净,但大概率被后端忽略——除非你明确写了自定义中间件去读 header。
- 推荐构造提交对象:
const postData = {...layui.form.val('your-filter'), csrf_token: token} - 如果用
$.ajax,data设为对象即可,jQuery 会自动序列化;设为字符串则要手动encodeURIComponent - 文件上传场景下,仍需保持
enctype="multipart/form-data",token 作为普通字段传,不用分两步
token 生命周期和刷新容易被绕过
最危险的不是“没加 token”,而是 token 长期有效、可复用、或绑定 session 失败。比如 PHP 中只用 $_SESSION['csrf_token'] 但没校验 session 是否已销毁,攻击者就能复用旧页面的 token。
每次加载表单页都该生成新 token,并在服务端绑定当前 session ID 和时间戳(例如 Redis key:csrf:{session_id}:{timestamp}),校验失败立即清空。
- 不要把 token 存 localStorage 或 sessionStorage:跨域页面可读,失去防伪造意义
- 避免前端用
Date.now()+ 固定 salt 拼 token:可预测,形同裸奔 - Layui 表单没提供“提交后自动禁用按钮”机制,需手动加
button.disabled = true防二次点击,但这只是体验层,不能替代服务端 token 销毁


















