v1.140.0
v1.140.0
Major security-focused reworks of the jwt, passwordhash, and passwordpwned packages. All three ship breaking API changes — see the Breaking Changes section before upgrading.
jwt — self-contained HMAC engine, verifier-based API, and security hardening
- Tokens are now signed and verified by a standard-library-only HMAC-SHA2 (HS256/HS384/HS512) implementation of the RFC 7515/7519 compact JWS, removing the
github.com/golang-jwt/jwt/v5build dependency (retained test-only for cross-validation). Restricting the surface to symmetric HMAC and verifying the signature before decoding claims makesalg=noneand algorithm-confusion attacks structurally impossible. - Pin the accepted signing algorithm and require
exp; validatenbfwith optional clock-skew leeway; enforceissand the fullaudset when configured. - Bound how long a session can be kept alive by renewals via an
auth_timeclaim preserved across renewals and a configurable maximum lifetime, so a stolen token cannot be renewed indefinitely. - Support HMAC key rotation through previous keys accepted for verification, validate minimum key length against the method hash size, and copy caller-supplied key material.
- Cap the login request body (413 on overflow) and reject unknown or trailing JSON; return generic client messages while logging details server-side; distinguish invalid credentials (401) from credential-backend failures (500).
- Emit RFC 6750
WWW-Authenticatechallenges on 401 responses and accept case-insensitive, multi-space Bearer prefixes per RFC 7235. - New API:
Authenticate,VerifyToken,IssueToken,MiddlewarewithClaimsFromContext, and optionsWithMaxBodyBytes,WithMaxSessionLifetime,WithClockSkewLeeway,WithPreviousKeys. - Fuzz tests for the login body and token parser, benchmarks for the issue and verify paths, and a runnable example; 100% statement coverage.
passwordhash — parameter envelope, rehash API, sentinel errors, and hardening
- Any hash the package can mint it can also verify: cost options clamp to the same [floor, ceiling] range the verification path accepts, and hashing rejects out-of-envelope configurations.
- Panics replaced with errors: invalid or zero-value
Paramsnow returnErrInvalidParamsinstead of panicking inside argon2. - Verification hardened against forged or corrupt blobs: encoded hashes larger than 16 KiB are rejected before decoding, deserialized parameters are validated against their bounds, and salt/key byte lengths are cross-checked against their declared sizes.
- New
PasswordNeedsRehashandEncryptPasswordNeedsRehashdetect hashes minted with outdated parameters, enabling transparent re-hashing on the next successful login. - Default parallelism is a flat 4 lanes (RFC 9106 §4) instead of
runtime.NumCPU(), so minted hashes are reproducible across heterogeneous hardware and a mixed fleet cannot enter a rehash loop; key and salt minimums for new hashes raised to 16 and 8 bytes. - Minimum-length policy is enforced only when hashing, not on verification, so raising the minimum no longer locks out existing users.
- Sentinel-error taxonomy matchable with
errors.Is:ErrPasswordTooShort,ErrPasswordTooLong,ErrInvalidParams,ErrInvalidHashData,ErrAlgoMismatch,ErrVersionMismatch,ErrInvalidPepperKey. - Derived keys and decrypted pepper material are wiped after comparison; fuzz, envelope round-trip, panic-safety, and boundary tests added; 100% coverage.
passwordpwned — breach-count API, hardened response validation, sentinel errors, and live health probe
- New
PwnedCountreturns the raw breach count for NIST-style threshold policies ("reject only if seen more than N times");IsPwnedPasswordis now a thin count>0 wrapper over it. - Decoded responses are capped at 8 MiB (configurable via
WithResponseSizeLimit) so a decompression bomb cannot exhaust memory, and are decoded according to their declaredContent-Encoding(brotli, gzip, identity) instead of assuming brotli. - Fail loud on garbage 200s: the body must begin with a structurally valid
<35-hex>:<count>range line, so a captive-portal page returnsErrMalformedResponseinstead of silently reading as "not pwned"; hash-suffix matching is case-insensitive, so lowercase-hex mirrors no longer produce silent false negatives. HealthCheckis now a live probe: a lightweight GET on a fixed range prefix bounded by a dedicated ping timeout (default 5 s,WithPingTimeout), never retried.- The HTTP retrier is built once in
New— invalid retry settings fail at construction — and a server-providedRetry-Afterheader is honored (capped at 60 s) on 429. - Sentinel-error taxonomy:
ErrInvalidURL,ErrInvalidUserAgent,ErrMalformedResponse,ErrResponseTooLarge,ErrUnsupportedEncoding,ErrUnexpectedStatus. - Tests pin the k-anonymity contract (only the 5-character hash prefix ever leaves the process), plus fuzz targets over the response parser and body reader; 100% coverage.
Other
- Fixed a flaky test timeout.
- Updated dependencies (including
golang.org/x/syncv0.22.0 andgolang.org/x/sysv0.47.0).
Breaking Changes
- jwt:
NewtakesVerifyCredentialsFn func(username, password string) (bool, error)instead ofUserHashFn— credential verification is fully delegated (pair it withpkg/passwordhash) and the package no longer performs bcrypt comparison itself.SigningMethodis now an HS256/HS384/HS512 enum,Claimscarries native fields instead of embeddingjwt.RegisteredClaims, andWithClaimSubjectis removed (thesubclaim is now the authenticated username). - passwordhash: default parallelism is a flat 4 instead of
runtime.NumCPU();WithKeyLen/WithSaltLenclamp to [16, 1024]/[8, 1024] andWithTime/WithMemorycap at 1024 passes/4 GiB (previously stored hashes remain verifiable unchanged).PasswordVerifyandEncryptPasswordVerifyno longer reject candidate passwords below the configured minimum length. InvalidParamsconfigurations that previously panicked now returnErrInvalidParams. - passwordpwned:
HealthChecknow performs a live request instead of always returning nil.Newrejects previously accepted configurations: URLs with a query or fragment, an empty or control-character User-Agent, zero retry attempts, and non-positive retry delays. A 200 response whose body is not valid HIBP range data now returnsErrMalformedResponseinstead of reporting the password as not pwned, andWithTimeoutignores non-positive values instead of disabling the client timeout.
Full Changelog: v1.139.0...v1.140.0