Go标准库net/http不提供CSRF防护,因其属应用层安全策略;gorilla/csrf是成熟方案,基于双重提交Cookie模式,需32字节密钥、正确挂载中间件并前端传递X-CSRF-Token。

为什么标准库的 http.Request 不自带 CSRF 防护
Go 标准库的 net/http 完全不处理 CSRF,它只负责底层 HTTP 通信。CSRF 是应用层安全策略,需要开发者显式集成。很多新手误以为启用 http.Cookie 的 HttpOnly 或 Secure 就能防 CSRF——其实这些只防 XSS 窃 cookie,对 CSRF 无效,因为 CSRF 攻击正是利用浏览器自动携带合法 cookie 发起请求。
用 gorilla/csrf 实现服务端 Token 管理
最成熟、轻量且被广泛验证的方案是 gorilla/csrf。它基于双重提交 Cookie 模式:服务端生成随机 token 存入 session(或加密签名后存 cookie),同时要求前端在表单中提交该 token(如 hidden 字段),并在每次 POST/PUT/DELETE 请求时校验一致性。
- 必须在所有写操作路由前挂载中间件:
csrf.Protect([]byte("32-byte-key")),密钥长度必须为 32 字节,否则 panic - HTML 表单中需插入
{{.CSRFField}}(模板渲染)或手动读取X-CSRF-Token响应头 +csrf.Token(r)函数生成值 - API 接口若用 JSON 提交,需在请求头带
X-CSRF-Token,且中间件默认只校验非 GET/HEAD/OPTIONS 方法
package main
import (
"html/template"
"net/http"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
)
var t = template.Must(template.New("base").Parse(`
`))
func handler(w http.ResponseWriter, r *http.Request) {
t.Execute(w, map[string]interface{}{"CSRFField": csrf.TemplateField(r)})
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", handler).Methods("GET")
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}).Methods("POST")
// 必须包裹整个路由树
http.ListenAndServe(":8080", csrf.Protect(
[]byte("12345678901234567890123456789012"), // 32-byte key
csrf.Secure(false), // 开发环境可关 HTTPS,生产务必设 true
csrf.HttpOnly(true),
)(r))
}
前端如何正确传递 Token(尤其 AJAX 场景)
CSRF 防护失效常因前端没传对 token。关键点:表单提交靠 hidden 字段;AJAX 请求必须从响应头或 DOM 中读取 token 并设进请求头,不能硬编码或复用旧值。
- 首次 GET 页面时,服务端会在响应头写入
X-CSRF-Token,值与csrf.Token(r)返回一致 - 后续 AJAX 请求需在
headers中设置X-CSRF-Token,且每次请求都应重新获取(token 可能轮换) - 避免把 token 存在 localStorage —— XSS 可读取,违背“不可预测+仅限 HTTP”原则;应依赖
httpOnlycookie 存服务端状态,前端只管传递
哪些情况会让 gorilla/csrf 失效或被绕过
不是加了中间件就万事大吉。常见失效点集中在配置和使用边界上:
立即学习“go语言免费学习笔记(深入)”;
-
csrf.Protect未包裹全部写操作路由(比如漏掉某个http.HandleFunc直接注册的 handler) - 开发时设
csrf.Secure(false),但部署到 HTTPS 站点后忘记改回true,导致 cookie 不被发送 - 前端用
fetch且未设credentials: 'include',浏览器不带 cookie,服务端拿不到关联的 token 状态 - 自定义 session 存储(如 Redis)未正确实现
Store接口的Save和Get,导致 token 无法持久化或校验失败
CSRF 的核心不在加密强度,而在 token 生命周期管理是否严格绑定用户会话、是否拒绝重复使用、是否隔离不同子域。哪怕用了 gorilla/csrf,如果 session 设计松散(比如 token 不绑定 IP 或 User-Agent),攻击面依然存在。










