feat(api): encrypt webhook signing secrets at rest - #81
Open
VladUZH wants to merge 1 commit into
Open
Conversation
WebhookEndpoint.secret was stored in plaintext — a DB leak handed an attacker the ability to forge valid signatures for every tenant's webhook consumer. Now AES-256-GCM under SECRET_ENCRYPTION_KEY: - lib/secret-crypto.ts: enc:v1:<iv>:<tag>:<ct> format, fresh IV per value, legacy plaintext passthrough on decrypt (7 unit tests incl. tamper-detection) - encrypt on endpoint creation; decrypt at both signing sites - production boot gate mirroring SESSION_SECRET; dev/test fall back to plaintext - scripts/encrypt-webhook-secrets.ts: idempotent one-time backfill - CI: test-api runs with a throwaway key to exercise the real path DEPLOY NOTE: set SECRET_ENCRYPTION_KEY on Railway before merging, then run the backfill once. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| throw new Error('Malformed encrypted secret'); | ||
| } | ||
| const [ivB64, tagB64, ctB64] = parts as [string, string, string]; | ||
| const decipher = createDecipheriv('aes-256-gcm', key, Buffer.from(ivB64, 'base64')); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes a long-standing known gap:
WebhookEndpoint.secretwas stored in plaintext, so a database leak handed an attacker valid-signature forgery against every tenant's webhook consumer.Design
SECRET_ENCRYPTION_KEY(64 hex chars); stored asenc:v1:<iv>:<tag>:<ct>, fresh IV per value/test)apps/api/scripts/encrypt-webhook-secrets.ts) is runSESSION_SECRET): refuses to start without the key, so a deploy can't silently write new secrets in plaintext; dev/test fall back to plaintext by designtest-apinow runs with a throwaway key so the integration suite exercises the real encrypt→decrypt pathSECRET_ENCRYPTION_KEY= output ofopenssl rand -hex 32DATABASE_URL+ the key:npx tsx scripts/encrypt-webhook-secrets.tsMerging before step 1 will fail the production boot gate and 502 the API.
🤖 Generated with Claude Code