不能直接用 gin.BasicAuth,因其不防时序攻击、密码明文暴露、未强制 HTTPS;硬编码或环境变量传密码易泄露;应自定义中间件,用 ConstantTimeCompare、安全加载密码、强制 HTTPS。

直接用 gin.BasicAuth 能快速保护后台,但默认实现不防时序攻击、密码明文暴露、没强制 HTTPS —— 这三件事会让认证在生产环境几乎无效。
为什么不能直接传 gin.Accounts{"admin": "123456"}
硬编码密码会进 Git 历史、二进制文件或内存 dump;从 os.Getenv 读取则会在 /proc/$PID/environ 中明文可见。哪怕只用于测试后台,也该避免裸密码。
- 改用常量时间比较:用
crypto/subtle.ConstantTimeCompare替代==比较密码字节 - 密码应从文件(如
/etc/secrets/basic-auth)或 Vault 加载,且文件权限设为0600 - 若必须用环境变量,至少用
syscall.Unsetenv在加载后立即清除
如何写一个防时序攻击的自定义 BasicAuth 中间件
官方 gin.BasicAuth 内部用的是简单字符串比较,无法抵抗基于响应时间的凭据爆破。自己实现时重点在解码和校验两步。
- 用
base64.StdEncoding.DecodeString解码Authorization头,捕获base64.CorruptInputError - 拆出
username:password后,对每个已知用户做subtle.ConstantTimeCompare(需将密码转为[]byte) - 无论匹配与否,都走完全部用户比较逻辑,避免响应时间泄露用户是否存在
- 校验通过后调用
c.Set(gin.AuthUserKey, username),后续 handler 才能用c.MustGet(gin.AuthUserKey).(string)
示例关键片段:
func CustomBasicAuth(accounts map[string][]byte) gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.GetHeader("Authorization")
if !strings.HasPrefix(auth, "Basic ") {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
decoded, err := base64.StdEncoding.DecodeString(auth[6:])
if err != nil {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
parts := strings.SplitN(string(decoded), ":", 2)
if len(parts) != 2 {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
user, pass := parts[0], []byte(parts[1])
valid := false
for u, pwd := range accounts {
if len(u) == len(user) && len(pwd) == len(pass) {
if subtle.ConstantTimeCompare([]byte(u), []byte(user)) == 1 &&
subtle.ConstantTimeCompare(pwd, pass) == 1 {
valid = true
c.Set(gin.AuthUserKey, u)
break
}
}
}
if !valid {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Next()
}
}
必须搭配 HTTPS,否则所有努力白费
Basic Auth 的密码在每次请求中都以 Base64 编码形式出现在 Authorization 头里——Base64 不是加密,只是编码。HTTP 明文传输等于把账号密码贴在快递单上寄出去。
- Nginx 前置时,在
location块里加add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" - Gin 内部重定向 HTTP → HTTPS 需手动判断
c.Request.TLS == nil并返回http.StatusMovedPermanently - 浏览器一旦缓存了 Basic Auth 凭据,就很难主动清除;HTTPS 至少保证它不会被中间人截获重放
真正难的不是写中间件,而是让密码不落地、不让时间差泄露信息、不让 HTTP 成为攻击入口——这三点漏掉任何一个,Basic Auth 就只剩“心理安慰”作用。


















