下载接口必须校验Cookie,因Echo中间件可能修改原始Cookies且Secure/HttpOnly属性需框架解密验证;正确做法是用c.Cookie()获取后做存在性、格式、时效三重校验,禁用手动遍历Request.Cookies()。

下载接口必须校验 Cookie,否则任意请求都能触发文件流出,等同于未授权访问。Echo 中不能靠 c.Request().Cookies() 手动遍历比对,必须用框架封装的 c.Cookie() 接口读取并配合显式校验逻辑。
为什么不能直接用 http.Request.Cookies() 读 Cookie
因为 Echo 的中间件链(如 session、JWT)可能已修改或覆盖原始 http.Request.Cookies 切片;更关键的是,若启用了 Secure/HttpOnly 等属性,手动解析 Header 可能漏掉框架内部的解密或签名验证步骤。常见错误是写成:
cookies := c.Request().Cookies()
for _, cookie := range cookies {
if cookie.Name == "auth_token" { ... }
}
这会绕过框架对 Cookie 值的完整性校验(比如签名失效时本该返回空),导致伪造 Cookie 成功通过。
正确读取并校验 Cookie 的三步法
所有校验必须基于 c.Cookie() 获取值,并立即做存在性、格式、时效三重判断:
- 先调用
c.Cookie("session_id"),捕获http.ErrNoCookie错误 —— 不存在就直接拒绝 - 拿到
*http.Cookie后,检查cookie.Value是否为空字符串或明显非法值(如含控制字符、长度超 512 字节) - 若该 Cookie 是服务端签发的(如 JWT 或加密 session),必须用相同密钥解码并校验
exp字段,不能只信客户端传来的MaxAge
下载 handler 中嵌入校验的最小可行写法
不要把校验逻辑抽到独立中间件里再跳转——下载响应需设置 Content-Disposition 和流式写入,中间件提前 return 会导致 header 冲突。直接在 handler 开头校验:
func downloadFile(c echo.Context) error {
cookie, err := c.Cookie("download_token")
if err != nil {
if errors.Is(err, http.ErrNoCookie) {
return echo.NewHTTPError(http.StatusUnauthorized, "missing download_token")
}
return echo.NewHTTPError(http.StatusBadRequest, "invalid cookie format")
}
// 校验 token 签名与时效(示例用 jwt-go)
token, err := jwt.Parse(cookie.Value, func(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !token.Valid {
return echo.NewHTTPError(http.StatusForbidden, "expired or tampered token")
}
// 后续打开文件、设置 header、c.Response().Write()
f, _ := os.Open("/path/to/file.zip")
defer f.Close()
c.Response().Header().Set("Content-Disposition", `attachment; filename="file.zip"`)
c.Response().Header().Set("Content-Type", "application/zip")
io.Copy(c.Response(), f)
return nil
}
容易被忽略的边界点
Cookie 名大小写敏感,"Download_Token" 和 "download_token" 是两个不同键;c.Cookie() 返回的 *http.Cookie 不包含 Domain 和 Path 的运行时匹配逻辑,这些由浏览器控制,服务端只需专注校验值本身;若下载接口允许跨域调用,Access-Control-Allow-Credentials: true 必须与 Access-Control-Allow-Origin 显式指定域名协同,不能为 *,否则浏览器会静默丢弃 Cookie。











