必须用gorilla/securecookie,因Echo.Context.SetCookie()仅写明文;需用32字节密钥初始化securecookie.Codecs,设HttpOnly、Secure、SameSite三属性,并正确处理ErrMacInvalid等解码错误。

必须用 gorilla/securecookie,echo.Context.SetCookie() 本身不加密
很多人误以为调用 c.SetCookie() 写入后浏览器拿不到明文,其实它只做标准 HTTP Cookie 设置,值完全裸露。如果你往 session_id 或 user_role 这类字段里写字符串,攻击者抓包改个值就能伪造身份。
真正能防篡改+防解密的只有带签名和加密的 codec,而 Echo 没内置这个能力——你得自己引入 gorilla/securecookie,并用它预处理值再塞进 Cookie。
怎么初始化一个安全的 securecookie.Codecs 实例
密钥长度和算法组合决定安全性底线。用短密钥或默认配置等于没加锁。
- 密钥必须 ≥ 32 字节:用
crypto/rand.Read()生成,别手写"my-secret-123"—— AES 块加密会直接 panic - 显式指定哈希 + 加密算法:
securecookie.GenerateRandomKey(32)只给随机字节,得手动套securecookie.New(hashKey, blockKey) - 至少启用 HMAC-SHA256 签名(防篡改)+ AES-256-CBC(防读取),禁用纯 base64 或 XOR 类“假加密”
- 实例建议全局复用,不要每次 handler 都 new 一个,避免 GC 和密钥泄露风险
c.GetCookie() 解密失败时怎么避免空指针或误判登录态
很多人写 val := c.GetCookie("session_id") 后直接 json.Unmarshal([]byte(val), &u),但 c.GetCookie() 成功只代表键存在;如果值被篡改或过期,securecookie.Decode() 会返回 err != nil,此时 val 是空字符串,Unmarshal 就 panic 或静默失败。
- 必须先 decode 再检查:
if err := scookie.Decode("session_id", cookieVal, &payload); err != nil { /* 处理 ErrMacInvalid / ErrDecryption */ } -
ErrMacInvalid表示签名失效(被篡改),ErrDecryption表示密钥不匹配或数据损坏,两者都该视为非法请求,直接c.Abort() - 解码出的
payload里应含exp字段,且必须和服务端当前时间比对,不能只信 Cookie 自带的Max-Age - 别把解密逻辑塞进每个 handler,抽成中间件统一处理,并早于鉴权中间件注册
为什么不能在中间件里用 c.Request().Cookie() 读原始值再加密
这是个隐蔽但高频的错误:有人想“先读再包一层”,结果调用 c.Request().Cookie("session_id") 后又用 securecookie.Encode() 写回去。问题在于,c.Request().Cookie() 返回的是原始未解码值,里面可能含 URL 编码、引号包裹或分号,直接传给 Encode() 会导致二次编码或解析错乱。
-
c.GetCookie()内部已做url.PathUnescape和空白清理,是唯一可信入口 - 若你坚持从
Header手动取,得先http.ParseCookie解析成*http.Cookie,再取.Value字段,否则引号、转义符全乱套 - 更糟的是,某些代理(如 Nginx)默认对 Cookie 值做自动 URL 解码,你本地测试正常,上线就 decode 失败
- 记住:加密只作用于业务 payload,不是作用于 HTTP Cookie 字符串格式本身
最易被忽略的一点:加密后的 Cookie 值体积会膨胀约 2–3 倍,若原本接近 4KB 上限,加密后极易超限被浏览器截断——这时解码必然失败,但错误日志里只显示 ErrMacInvalid,排查时容易绕远路。上线前务必用真实密钥压测长度。










