Go密码哈希必须用golang.org/x/crypto/bcrypt,cost建议12或14;比对须用CompareHashAndPassword防时序攻击;Cookie仅存随机session ID,设HttpOnly、Secure、SameSite;session存储上线必用Redis;登录需CSRF防护,API场景可用JWT替代。
golang.org/x/crypto/bcrypt 安全哈希密码明文存密码是硬性红线,Go 没有内置 bcrypt,必须用官方推荐的 golang.org/x/crypto/bcrypt。直接 bcrypt.GenerateFromPassword 生成哈希时,cost 参数别写死成 10——它影响 CPU 时间,现代服务器建议用 12 或 14;太低不安全,太高拖慢登录响应。
常见错误:把用户输入的原始密码
和数据库里已哈希的值直接比对(==),结果永远失败。必须用 bcrypt.CompareHashAndPassword,它自带防时序攻击逻辑。
hash, err := bcrypt.GenerateFromPassword([]byte("user123"), 12)
if err != nil {
log.Fatal(err)
}
// 存入数据库的 hash 是类似 "$2a$12$..." 的字符串
err = bcrypt.CompareHashAndPassword(hash, []byte("user123")) // nil 表示匹配成功
http.Cookie + http.SetCookie 管理会话标识不要把用户 ID、角色等敏感信息塞进 Cookie 值里明文传;只放一个随机、不可预测的会话 ID(session ID),后端查表映射到真实用户数据。Cookie 必须设 HttpOnly(防 XSS 读取)、Secure(仅 HTTPS 传输)、SameSite=Strict 或 Lax(防 CSRF)。
容易忽略的点:MaxAge 和 Expires 别同时设;优先用 MaxAge(秒数),它更可靠;Expires 在某些客户端时区错乱时会出问题。
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: "abc123xyz", // 应为 crypto/rand 生成的 32 字节 base64
MaxAge: 3600, // 1 小时
HttpOnly: true,
Secure: true, // 仅限 HTTPS
SameSite: http.SameSiteLaxMode,
})
开发阶段可用 sync.Map 模拟 session 存储,但上线必须换 Redis 或其他持久化方案——否则进程重启 session 全丢,且多实例部署时无法共享会话。
关键设计点:
UserID int、ExpiresAt time.Time、IP string 等)ExpiresAt,过期就清除并返回 401别用普通 map,并发读写 panic;也别在 handler 里直接操作全局 map——加锁复杂且易漏,sync.Map 是底线选择。
如果登录走 HTML 表单(POST /login),没 CSRF Token 就等于裸奔。前端需在 hidden input 里带 token,后端用 gorilla/csrf 或自己维护 token store 校验。
常见疏漏:
SameSite=Strict,反而扩大攻击面纯 API 场景可依赖 Authorization: Bearer + 短期 JWT,但 JWT 自带签名验证,别手写解析逻辑。