Download internal/httpauth/httpauth.go from a3216/gcli2api: direct link, hf CLI and curl.
- Browser
- Download file 1.69 kB
-
https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/httpauth/httpauth.go
- Command line
-
hf download hf://spaces/a3216/gcli2api/internal/httpauth/httpauth.go
-
curl -L -o httpauth.go https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/httpauth/httpauth.go
1.69 kB
| // Package httpauth 网关与面板共用的 Bearer 鉴权原语。 | |
| // | |
| // 单独成包的原因:server(/v1/*、/status)与 panel(/panel/api/*)两处鉴权 | |
| // 必须完全同口径——此前各自复制了一份"字符串直接比较"的实现,既容易漂移, | |
| // 又都带计时侧信道。统一到这里后,口径只有一份,且天然常量时间比较。 | |
| package httpauth | |
| import ( | |
| "crypto/sha256" | |
| "crypto/subtle" | |
| "net/http" | |
| "strings" | |
| ) | |
| // bearerPrefix 认证方案前缀(大小写敏感,与 HTTP 规范及既有实现一致)。 | |
| const bearerPrefix = "Bearer " | |
| // VerifyBearer 校验请求头是否携带正确的 Bearer 密钥。 | |
| // | |
| // key 为空表示"未启用鉴权",恒返回 true(调用方据此放行)。 | |
| // 比较用 SHA-256 摘要 + subtle.ConstantTimeCompare: | |
| // - 常量时间,不因前缀匹配长度而泄露信息; | |
| // - 先摘要再比较,长度差异被吸收进摘要(不会因长度不同提前返回); | |
| // - 摘要本身不可逆,即便有侧信道也拿不到密钥原文。 | |
| func VerifyBearer(r *http.Request, key string) bool { | |
| if key == "" { | |
| return true | |
| } | |
| authz := r.Header.Get("Authorization") | |
| if !strings.HasPrefix(authz, bearerPrefix) { | |
| // 缺头/方案不对:仍走一次摘要比较,保持耗时形状一致。 | |
| subtle.ConstantTimeCompare(digest(""), digest(key)) | |
| return false | |
| } | |
| tok := authz[len(bearerPrefix):] | |
| return subtle.ConstantTimeCompare(digest(tok), digest(key)) == 1 | |
| } | |
| // digest 返回 s 的 SHA-256(定长 32 字节,供常量时间比较)。 | |
| func digest(s string) []byte { | |
| sum := sha256.Sum256([]byte(s)) | |
| return sum[:] | |
| } | |