Security Best Practices
Production hardening checklist for bx-jwt — allowlists, key sizes, clock skew, and common pitfalls.
JWTs are easy to use but easy to misuse. This page collects the production hardening guidance specific to bx-jwt — most are one-line configuration changes.
1. Reject alg:none (Built-in)
bx-jwt unconditionally rejects unsigned (alg:none) tokens. There is no setting to disable this. Any attempt to verify or refresh such a token throws bxjwt.JWTVerificationException.
2. Lock Down the Algorithm Allowlist
Algorithm-confusion attacks rely on the verifier accepting an algorithm the issuer never intended (the classic example is asking an RSA verifier to treat a public key as an HMAC secret). Cut the attack off by enumerating exactly the algorithms you expect:
// ModuleConfig.bx
allowedAlgorithms = [ "HS256", "RS256" ]Any token signed with anything else throws JWTVerificationException. Leaving the array empty allows every algorithm the module supports — fine for prototypes, not for production.
3. Use Compliant HMAC Key Sizes
RFC 7518 §3.2 defines minimum HMAC key lengths and bx-jwt enforces them at parse time:
HS256
32 bytes (256 bits)
HS384
48 bytes (384 bits)
HS512
64 bytes (512 bits)
Use jwtGenerateSecret( bits ) to always produce a compliant key. Hand-typed strings almost always fail this check.
4. Tune Clock Skew Per Environment
clockSkew (default: 60 seconds) absorbs drift between distributed services without opening a large vulnerability window. Tune it down for security-sensitive flows, up for far-flung clusters:
5. Always Assert Issuer / Audience
A token signed by auth-service for the analytics audience should not authenticate against the payments API. Pin both:
Or set defaultIssuer / defaultAudience in ModuleConfig.bx so they are auto-injected on issue.
6. Keep Lifetimes Short
Short-lived access tokens (minutes to ~1 hour) limit the blast radius of theft.
Pair with a refresh flow using
jwtRefresh()andallowExpired: truefor graceful renewal.Always set
exp— either explicitly in the payload, with.expireIn()on the builder, or via thedefaultExpirationsetting.
7. Stamp kid and Verify Through It
For any deployment that may rotate keys (i.e. all of them), stamp a kid header on every signed token and look the key up at verify time. See Key Rotation for the full pattern.
8. Never Trust jwtDecode() Output
jwtDecode() is read-only — it does not verify signatures. Use it to pick a kid or detect the alg, then always follow up with jwtVerify() before acting on any claim.
9. Encrypt PII / PHI With JWE
For payloads containing sensitive personal data, use JWE instead of (or in addition to) JWS. For both integrity and confidentiality, sign first then encrypt — the nested JWT pattern.
10. Store Keys Outside the Codebase
Use the keys registry with ${env.VAR} substitution, or load keys from a KMS / Vault at runtime via JWTService.registerKey(). Keys committed to source control should be considered compromised.
Quick Checklist
Related
Last updated
Was this helpful?
