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

Cookie 加密必须用 securecookie,不能靠自己拼接
Echo 本身不提供 Cookie 加密能力,echo.Context.SetCookie() 写入的是明文;若需加密(比如存用户 ID、角色等敏感字段),必须引入 gorilla/securecookie 或类似库手动加解密。常见错误是把加密逻辑写在 handler 里、用 base64.StdEncoding.EncodeToString() 或简单 XOR 处理——这些既不防篡改也不防重放,浏览器拿到原始 Cookie 后可随意解码修改。
正确做法是:初始化一个 securecookie.Codecs 实例,传入至少 32 字节的密钥(crypto/rand.Read() 生成),并显式指定哈希和块加密算法:
var cookieHandler = securecookie.New(
securecookie.GenerateRandomKey(32), // Auth key
securecookie.GenerateRandomKey(32), // Encrypt key
).DecodeFunc(securecookie.DecodeMulti)之后在写入前编码:
encoded, err := cookieHandler.Encode("user_id", userID)
if err != nil { /* handle */ }
c.SetCookie(&http.Cookie{
Name: "user_id",
Value: encoded,
Path: "/",
HttpOnly: true,
Secure: true, // 仅 HTTPS 传输
SameSite: http.SameSiteLaxMode,
})
c.SetCookie() 的 Expires 和 MaxAge 别同时设
浏览器对同时设置 Expires 和 MaxAge 的 Cookie 行为不一致:Chrome 优先认 MaxAge,Safari 可能忽略 MaxAge 而只看 Expires,导致过期时间错乱。Echo 的 http.Cookie 结构体允许两者都赋值,但实际使用中必须二选一。
- 设
MaxAge > 0:相对过期(秒数),推荐用于会话型 Cookie(如登录态) - 设
Expires为未来时间点:绝对过期,适合长期凭证(如“记住我”) - 设
MaxAge = 0:会话 Cookie,关闭浏览器即失效 - 设
MaxAge :立即删除(等效于 <code>Expires = time.Now().Add(-1))
注意:MaxAge 是 int 类型,不是 time.Duration;别误写成 MaxAge: time.Hour —— 这会编译失败或被截断为 0。
读取加密 Cookie 时要检查 err == securecookie.ErrMacInvalid
解码失败不等于“Cookie 不存在”。用 cookieHandler.Decode("user_id", cookieValue, &userID) 时,可能返回三种典型错误:
-
http.ErrNoCookie:请求里压根没这个 Cookie 名(c.Cookie("user_id")返回) -
securecookie.ErrMacInvalid:Cookie 值被篡改或密钥不匹配(最常见于部署新密钥后旧 Cookie 未清理) - 其他 error:比如类型不匹配(
userID是int,但存的是字符串)
别把 ErrMacInvalid 当成普通错误吞掉或返回 500;应视同未登录处理(跳转登录页或返回 401),否则攻击者反复提交非法值可能触发日志刷屏或埋点干扰。
HTTP Only + SameSite + Secure 三者缺一不可
哪怕用了加密,若 Cookie 属性配置不当,依然会被 XSS 或 CSRF 利用:
-
HttpOnly: true:阻止 JavaScript 读取(防 XSS 窃取) -
Secure: true:强制仅 HTTPS 传输(防中间人窃听) -
SameSite: http.SameSiteStrictMode或http.SameSiteLaxMode:限制跨站请求携带(防 CSRF)
特别注意:SameSite=Strict 在用户从外部链接点击进入站点时会丢失 Cookie,Lax 更实用;但若你的接口明确要求跨域 POST(如支付回调),就得配合 credentials: 'include' 和 CORS 配置,不能只依赖 SameSite。
加密只是让 Cookie 内容不可读,属性配置才是守住边界的最后一道门——密钥再长,漏了 HttpOnly 就等于把钥匙挂在门把手上。


















