Files
curo1305 e2c55556ac Switch JWT signing from HS256 to RS256 (4096-bit RSA)
- Replace symmetric SECRET_KEY with JWT_PRIVATE_KEY / JWT_PUBLIC_KEY (PEM)
- Add iat claim to every token
- Add expand_newlines validator in config for single-line .env PEM values
- Add scripts/generate_jwt_keys.py key-generation helper
- Update security-auditor agent JWT checklist with RS256 enforcement rules
- Mark RS256 as done in TODO.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 23:00:35 +02:00

1.5 KiB

2026-04-13 — Switch JWT signing to RS256 (4096-bit RSA)

Timestamp: 2026-04-13T05:00:00

Summary

Replaced symmetric HS256 JWT signing with asymmetric RS256 using a 4096-bit RSA key pair. The private key signs tokens; the public key verifies them. Added iat (issued-at) claim to every token and a key-generation helper script.

Motivation

HS256 uses the same secret for signing and verification — if the key leaks, an attacker can forge arbitrary tokens. RS256 with a 4096-bit key eliminates this: the private key never leaves the backend process, and the public key can be distributed safely.

Files Added / Modified

  • scripts/generate_jwt_keys.py — generates a 4096-bit RSA key pair; outputs single-line PEM values ready to paste into backend/.env
  • backend/app/core/config.py — replaced SECRET_KEY / ALGORITHM=HS256 with JWT_PRIVATE_KEY, JWT_PUBLIC_KEY, ALGORITHM=RS256; added expand_newlines validator to handle \n-escaped PEM in .env
  • backend/app/core/security.pycreate_access_token now signs with JWT_PRIVATE_KEY and includes iat claim; decode_access_token verifies with JWT_PUBLIC_KEY and pins algorithms=["RS256"]
  • .env.example — removed SECRET_KEY, added JWT_PRIVATE_KEY and JWT_PUBLIC_KEY placeholders with generation instructions
  • .claude/agents/security-auditor.md — updated JWT checklist: added checks for wrong algorithm (non-RS256), symmetric key usage, and key-from-env requirement; updated policy note
  • TODO.md — added RS256 item to Auth/session security section (checked off)