echo-contrib/csrf中间件必须在路由注册前调用,否则所有POST请求因校验失败返回403;需全局启用、匹配\_csrf字段、正确配置Cookie属性(SameSite=Lax/Strict、HttpOnly=false、HTTPS下加Secure),校验失败不进业务逻辑且静默拒绝。

echo-contrib/csrf 中间件必须在路由注册前调用
中间件注册顺序错误是 403 Forbidden 最常见的原因。如果你看到所有 POST 表单提交都失败,且日志里没有业务逻辑执行痕迹,大概率是 e.Use(csrf.New()) 放在了 e.POST() 之后。
这是因为 Echo 的中间件链是「先注册、后生效」,请求进入时按注册顺序依次执行。CSRF 中间件需要在路由分发前就介入,才能读取 Cookie、解析表单、校验 token。一旦漏掉这步,后续 Handler 根本收不到请求。
-
go get github.com/labstack/echo-contrib/csrf是唯一必需的依赖,别用 gorilla/csrf 替代——它不兼容 Echo 的上下文模型 - 初始化后立即调用
e.Use(csrf.New()),推荐放在main()函数中e := echo.New()下一行 - 不要试图在某个特定路由上局部启用,CSRF 防护必须全局生效,否则未覆盖的 POST 路由就是裸奔入口
HTML 表单里的 _csrf 字段名不能改
CSRF 中间件默认只从请求体或 query string 中提取名为 _csrf 的值。哪怕你模板里写成 csrf_token 或后端配置了自定义字段名,只要没显式传参覆盖,默认行为就是硬编码匹配 _csrf。
这个字段必须是 <input type="hidden">,且嵌入在 <form method="POST"> 内部。放在外面、用 JS 动态注入、或者通过 URL 参数传(比如 ?_csrf=xxx),中间件都能识别,但表单场景下最稳的方式仍是隐藏字段。
立即学习“go语言免费学习笔记(深入)”;
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 模板中直接写
<input type="hidden" name="_csrf" value="{{.CSRFToken}}">,{{.CSRFToken}}由中间件自动注入c.Render上下文 - 如果用 JSON API,需手动从
c.Request()提取:token := csrf.Token(c.Request()),再通过c.JSON返回给前端 - 别把 token 存进 localStorage 后再塞进请求头——CSRF 中间件默认不看 header,除非你用
csrf.WithHeader显式开启
Cookie 的 SameSite 和 HttpOnly 必须配对设置
只生成 token 不够,cookie 本身若配置不当,会让整个防护形同虚设。攻击者可能通过 XSS 窃取 token,或利用浏览器跨站携带机制绕过校验。
关键点在于:CSRF token 需要被前端 JS 读取并填入表单,所以 cookie 不能设 HttpOnly=true;但又必须防止被第三方站点读取,所以 SameSite 至少设为 Lax(转账类操作建议 Strict)。
- 默认情况下
echo-contrib/csrf发放的 cookie 是SameSite=Lax+HttpOnly=false,符合常规表单场景 - 如果启用了 HTTPS,务必加
Secure=true参数,否则浏览器拒绝发送该 cookie - 别手动覆盖 cookie 名称(如改成
my_csrf_cookie),中间件内部硬依赖默认名_gorilla_csrf,改了就无法关联 session
CSRF 校验失败时不会进业务 Handler,也不触发 panic
这是很多人误以为「中间件没起作用」的原因:请求直接返回 403,控制台没报错,日志里也看不到你的 fmt.Println("in handler")。它压根没走到你写的 func(c echo.Context) error 里。
校验失败只有一种结果:HTTP 状态码 403 + 空响应体。没有重定向、不执行任何业务逻辑、也不会调用 c.Error()。这种静默拒绝正是设计意图——避免暴露服务端状态或逻辑细节。
- 调试时可在中间件前加个日志中间件,确认请求是否抵达
csrf.New() - 检查浏览器开发者工具 Network 面板,看 POST 请求的 Response Headers 是否含
Set-Cookie: _gorilla_csrf=... - 如果 token 过期(默认 24 小时),用户刷新页面即可获得新 token;无需后端干预,但前端应处理 403 并提示“请重试”

















