diff --git a/internal/auth/auth.go b/internal/auth/auth.go index f311c0c..e75d6aa 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -40,7 +40,15 @@ func HashPassword(password string) (string, error) { ), nil } -// VerifyPassword 按 PHC 串内参数重派生并常量时间比较。任何解析失败均返回 false。 +// PHC 参数上下界(防畸形哈希触发 argon2 panic 或 OOM,评审 round2 P2-8): +// t/p 过小使 argon2.IDKey panic,m 过大直接耗尽内存。 +const ( + maxArgonTime = 10 + maxArgonThreads = 8 + maxArgonMemory = 1 << 20 // KiB(1 GiB),远大于现行 19456 +) + +// VerifyPassword 按 PHC 串内参数重派生并常量时间比较。任何解析失败或参数越界均返回 false。 func VerifyPassword(encoded, password string) bool { parts := strings.Split(encoded, "$") // ["", "argon2id", "v=19", "m=..,t=..,p=..", salt, hash] @@ -57,12 +65,16 @@ func VerifyPassword(encoded, password string) bool { if _, err := fmt.Sscanf(parts[3], "m=%d,t=%d,p=%d", &m, &t, &p); err != nil { return false } + if t < 1 || t > maxArgonTime || p < 1 || p > maxArgonThreads || + m < 8 || m > maxArgonMemory { + return false + } salt, err := base64.RawStdEncoding.DecodeString(parts[4]) if err != nil { return false } want, err := base64.RawStdEncoding.DecodeString(parts[5]) - if err != nil { + if err != nil || len(want) != KeyLen { return false } got := argon2.IDKey([]byte(password), salt, t, m, p, uint32(len(want))) diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go index ced23c0..58de392 100644 --- a/internal/auth/auth_test.go +++ b/internal/auth/auth_test.go @@ -31,6 +31,26 @@ func TestPasswordHashRoundtrip(t *testing.T) { } } +// TestVerifyPasswordMalformedParams 畸形 PHC 参数安全返回 false: +// t/p=0 会使 argon2.IDKey panic、m 巨大会 OOM(评审 round2 P2-8)。 +func TestVerifyPasswordMalformedParams(t *testing.T) { + salt := "AAAAAAAAAAAAAAAAAAAAAA" // 16 字节 base64(raw std) + hash := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // 32 字节 + bad := []string{ + "$argon2id$v=19$m=19456,t=0,p=1$" + salt + "$" + hash, + "$argon2id$v=19$m=19456,t=2,p=0$" + salt + "$" + hash, + "$argon2id$v=19$m=999999999,t=2,p=1$" + salt + "$" + hash, + "$argon2id$v=19$m=19456,t=999,p=1$" + salt + "$" + hash, + "$argon2id$v=19$m=19456,t=2,p=99$" + salt + "$" + hash, + "$argon2id$v=19$m=19456,t=2,p=1$" + salt + "$AAAA", // 哈希长度异常 + } + for _, h := range bad { + if VerifyPassword(h, "x") { + t.Errorf("畸形参数不应通过: %s", h) + } + } +} + func TestTokenAndStrength(t *testing.T) { tok, err := NewToken() if err != nil {