mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 13:51:36 +00:00
fix(oidc/prelogin): encrypt state/nonce/PKCE-verifier at rest (HIGH-5)
Pre-login rows previously persisted the OIDC state, nonce, and PKCE
verifier as plaintext columns; an operator restoring an unredacted
backup of oidc_pre_login_sessions to a debug environment leaked every
in-flight handshake. If the IdP also leaked the auth code in the same
window (logged at a misconfigured TLS terminator, etc.), the attacker
could exchange code + verifier directly. RFC 7636 §7 requires verifier
confidentiality.
This commit:
- Migration 000041 adds {state,nonce,pkce_verifier}_enc BYTEA columns
and makes the legacy plaintext columns nullable. A follow-up
migration drops the plaintext columns once the rolling deploy
completes.
- internal/repository/postgres/oidc_prelogin.go::Create encrypts the
three secrets via crypto.EncryptIfKeySet (v3 magic 0x03 + per-row
salt + nonce + AES-256-GCM tag) and writes only the encrypted
columns; legacy plaintext stays NULL on the write path.
- LookupAndConsume prefers encrypted columns via materialize(),
falling back to the legacy plaintext only when _enc is NULL — the
rolling-deploy compat layer that 000042 will retire.
- NewPreLoginRepository takes encryptionKey; cmd/server/main.go threads
cfg.Encryption.ConfigEncryptionKey in.
- Encryption key reuses CERTCTL_CONFIG_ENCRYPTION_KEY (same passphrase
already protecting OIDC client secrets and SessionSigningKey material).
No new env var.
Why encryption-at-rest, not HMAC: the spec's HMAC approach required
moving plaintext into the cookie (the cookie currently carries only
row ID + HMAC). Re-shaping the cookie wire format would be a larger
refactor; the audit explicitly admits encryption-at-rest is an
acceptable closure (weaker because backups still contain decryptable
ciphertext, but the encryption key is held separately from the DB
backup, and the 10-minute TTL further bounds usable secret window).
Three new regression tests in oidc_prelogin_encryption_test.go pin:
(a) _enc columns contain v3-format ciphertext, NOT plaintext
substrings, post-Create
(b) legacy plaintext columns are NULL post-Create (defends against
future patches that re-introduce plaintext writes)
(c) LookupAndConsume round-trips state/nonce/verifier byte-for-byte
A fourth test pins the legacy-row fallback for rolling-deploy compat.
Refs: cowork/auth-bundles-audit-2026-05-10.md HIGH-5
Spec: cowork/auth-bundles-fixes-2026-05-10/09-high-5-prelogin-secret-protection.md
This commit is contained in:
+4
-1
@@ -396,7 +396,10 @@ func main() {
|
||||
oidcProviderRepo := postgres.NewOIDCProviderRepository(db)
|
||||
oidcMappingRepo := postgres.NewGroupRoleMappingRepository(db)
|
||||
oidcUserRepo := postgres.NewUserRepository(db)
|
||||
oidcPreLoginRepo := postgres.NewPreLoginRepository(db)
|
||||
// Audit 2026-05-10 HIGH-5: thread CERTCTL_CONFIG_ENCRYPTION_KEY into the
|
||||
// pre-login repo so state/nonce/PKCE-verifier are encrypted at rest. Same
|
||||
// key already protects OIDC client secrets and session signing keys.
|
||||
oidcPreLoginRepo := postgres.NewPreLoginRepository(db, cfg.Encryption.ConfigEncryptionKey)
|
||||
preLoginAdapter := oidcsvc.NewPreLoginAdapter(
|
||||
oidcPreLoginRepo,
|
||||
sessionKeyRepo, // Phase 4 SessionSigningKeyRepository
|
||||
|
||||
Reference in New Issue
Block a user