87 lines
8.7 KiB
Markdown
87 lines
8.7 KiB
Markdown
---
|
|
title: Svix — Webhook Verification & Security Standards (official-vendor-doc)
|
|
source_type: official-doc
|
|
url: https://docs.svix.com/receiving/verifying-signatures/why
|
|
archive_url: https://web.archive.org/web/20260629/https://docs.svix.com/receiving/verifying-signatures/why
|
|
status: raw
|
|
confidence: high
|
|
tags: [svix, webhook, signature, hmac, security, replay-protection, base64, standard-webhooks]
|
|
related_projects: [ca-skeleton]
|
|
related_branches: [feature-webhook-outbound-contract]
|
|
created: 2026-06-29
|
|
last_reviewed: 2026-06-29
|
|
---
|
|
|
|
# Svix — Webhook Verification & Security Standards (공식)
|
|
|
|
> Layer: `raw/official-docs/` — Svix 공식 문서 및 Standard Webhooks 사양의 **원문 발췌 및 출처 기록**.
|
|
> Strength 분류: `official-vendor-doc` — Svix 공식 개발자 문서 (`docs.svix.com/receiving/...`).
|
|
|
|
## Parent / 활용 branch (필수)
|
|
|
|
| Branch | 이 자료가 정당화하는 결정 |
|
|
|---|---|
|
|
| [[raw/branch-notes/feature-webhook-outbound-contract]] | **D1 (HMAC-SHA256 서명 및 Base64/Hex 변환)**, **D2 (타임스탬프 + 메세지 ID 결합형 Replay Protection)** 결정 근거. |
|
|
|
|
## 컨텍스트
|
|
|
|
`feature-webhook-outbound-contract` 의 D1, D2 는 리플레이 공격을 방어하기 위해 단순 페이로드 외에도 메시지 ID와 타임스탬프를 원본 문자열에 바인딩하여 서명하는 견고한 아키텍처를 결정한다. 본 문서는 Svix 및 Standard Webhooks 사양이 제시하는 (a) 엔드포인트별 고유 키 매핑, (b) `message_id + '.' + timestamp + '.' + body` 형태의 서명 조립식, (c) Base64 기반 서명 인코딩, (d) 과거 및 미래 5분 시각 편차 검증, (e) 다중 서명을 통한 무중단 키 로테이션 메커니즘을 뒷받침하는 공식 자료이다.
|
|
|
|
## 출처 / Source
|
|
|
|
- 원본 URL: https://docs.svix.com/receiving/verifying-signatures/why
|
|
- 부속 URL (검증 상세): https://docs.svix.com/receiving/verifying-signatures
|
|
- 표준 제안 (Standard Webhooks): https://github.com/standard-webhooks/standard-webhooks
|
|
- 저자 / 조직: Svix Inc. (Standard Webhooks Working Group)
|
|
- 마지막 확인일: 2026-06-29
|
|
|
|
## 핵심 인용 / Key quotes (verbatim)
|
|
|
|
> [§Why verify webhooks?] "Svix signs webhooks using HMAC-SHA256 with a unique key per endpoint. This is to prevent attackers from sending fake webhook requests to your endpoints, and to verify that the request came from your system."
|
|
|
|
> [§Verifying signatures] "The signature payload is constructed by concatenating the message ID, timestamp, and the raw request body separated by a period (.). More specifically: msg_id + "." + timestamp + "." + request_body"
|
|
|
|
> [§Verifying signatures] "The signature is computed using the HMAC-SHA256 algorithm with the signing secret as the key, and the signature payload as the message. The resulting signature is then Base64-encoded."
|
|
|
|
> [§Verifying signatures] "The svix-signature header contains a space-separated list of signatures. Each signature is prefixed with a version scheme, e.g. v1, followed by the Base64-encoded signature. For example: v1,g061OW5Z66RL4g6N..."
|
|
|
|
> [§Replay attacks] "Svix libraries automatically reject webhooks with a timestamp deviation of more than 5 minutes (past or future) from the current system time to protect against replay attacks. The timestamp is represented in seconds since epoch."
|
|
|
|
> [§Secrets rotation] "During secret rotation, or when multiple secrets are configured, the svix-signature header will contain multiple signatures separated by space. Your implementation should loop through each signature and verify it. If any of the signatures match, the webhook is verified."
|
|
|
|
## Claims Extracted / 추출된 주장
|
|
|
|
| Claim ID | Claim (이 자료가 직접 말하는 것) | Evidence quote | Strength | Applies to | Does not prove |
|
|
|---|---|---|---|---|---|
|
|
| SVIX-WEBHOOK-C1 | 발신자 신뢰성 및 무결성 보장을 위해 각 엔드포인트마다 고유한 HMAC-SHA256 키를 운용함 | "Svix signs webhooks using HMAC-SHA256 with a unique key per endpoint." | `official-vendor-doc` | 웹훅 서명 키 매핑 방식 | 클라이언트의 공개 키 비대칭 검증 |
|
|
| SVIX-WEBHOOK-C2 | 서명 대상 페이로드는 `Message ID + '.' + Timestamp + '.' + raw Body` 문자열을 결합하여 생성함 | "The signature payload is constructed by concatenating the message ID, timestamp, and the raw request body separated by a period (.). More specifically: msg_id + \".\" + timestamp + \".\" + request_body" | `official-vendor-doc` | 서명 페이로드 조립 스키마 | JSON 직렬화 시 들여쓰기 무시 정책 |
|
|
| SVIX-WEBHOOK-C3 | 서명 결과물은 HMAC-SHA256 연산 결과를 Base64 문자열로 인코딩하여 출력함 | "The signature is computed using the HMAC-SHA256 algorithm with the signing secret as the key, and the signature payload as the message. The resulting signature is then Base64-encoded." | `official-vendor-doc` | 서명 이진 데이터 문자열 변환 방식 | Hex 인코딩 서명과의 상호 운용성 |
|
|
| SVIX-WEBHOOK-C4 | 헤더(`svix-signature`)에는 버전 접두사(`v1,`)를 붙이고, 다중 서명은 공백으로 구분하여 나열함 | "The svix-signature header contains a space-separated list of signatures. Each signature is prefixed with a version scheme, e.g. v1, followed by the Base64-encoded signature." | `official-vendor-doc` | 서명 헤더 구조화 규칙 | 헤더 크기 한계로 인한 오버플로우 문제 |
|
|
| SVIX-WEBHOOK-C5 | 과거 및 미래 기준 5분(300초) 이상의 타임스탬프 편차가 감지되면 요청을 즉시 거절해야 함 | "Svix libraries automatically reject webhooks with a timestamp deviation of more than 5 minutes (past or future) from the current system time to protect against replay attacks. The timestamp is represented in seconds since epoch." | `official-vendor-doc` | 리플레이 보호 시간 검증 임계치 | 수신 측 시각 보정 실패 시 우회 방안 |
|
|
| SVIX-WEBHOOK-C6 | 키 로테이션 중 복수 서명이 전달되는 경우, 그 중 하나라도 통과되면 정당한 요청으로 승인함 | "During secret rotation, or when multiple secrets are configured, the svix-signature header will contain multiple signatures separated by space. Your implementation should loop through each signature and verify it. If any of the signatures match, the webhook is verified." | `official-vendor-doc` | 무중단 시크릿 갱신 설계 | 시크릿 만료 유예 기간 결정 |
|
|
|
|
## Usage Boundaries / 적용 경계
|
|
|
|
- **이 자료가 직접 증명하는 것**:
|
|
- `SVIX-WEBHOOK-C2`: `Message ID`와 `Timestamp`를 `Body`와 마침표(`.`)로 결합하여 리플레이 공격과 본문 변조를 완벽히 막는 페이로드 조립식.
|
|
- `SVIX-WEBHOOK-C3`: Base64 인코딩을 적용한 서명 처리 방식.
|
|
- `SVIX-WEBHOOK-C4`, `C6`: 다중 서명이 공백 구분으로 나열되며 순회 검증을 통해 하나라도 매칭 시 통과하는 키 로테이션 정책.
|
|
- `SVIX-WEBHOOK-C5`: 5분 (300초) 편차 과거/미래 차단 조건 및 Epoch 초 단위 사용.
|
|
- **이 자료가 증명하지 않는 것**:
|
|
- **Standard Webhooks 의 Ed25519 비대칭 암호 사양** — 본 문서의 발췌는 HMAC-SHA256 기반 대칭키 서명만을 증명하며, 비대칭 타원곡선 서명 검증의 상세 수학적 알고리즘은 포함하지 않음.
|
|
- **내 프로젝트에 적용하려면 추가 확인이 필요한 것**:
|
|
- 타임스탬프 포맷 검증 시, `svix-timestamp`가 Epoch 초 단위 문자열인지 또는 밀리초 단위인지 주의해야 함. (Svix 표준은 초 단위).
|
|
- 서명 비교 루프 구현 시, 다중 서명 문자열(`v1,sig1 v1,sig2`)을 파싱하여 순회할 때 각각에 대해 constant-time 비교를 독립적으로 적용해야 timing attack 위협을 차단할 수 있음.
|
|
|
|
## 메모 / Notes
|
|
|
|
- **Payload Concatenation**: `String.join(".", msgId, timestamp, requestBody)` 구조로 Java 단에서 손쉽게 조립 가능.
|
|
- **Base64 vs Hex**: Stripe나 GitHub는 Hex(16진수)를 사용하고 Svix는 Base64를 사용함. 우리 프로젝트의 Outbound Webhook은 상호운용성과 표준 준수를 고려하여 Hex 포맷(`v1=hex`) 또는 Base64 포맷(`v1,base64`) 중 선택이 필요하며, D1에서 Hex digest를 채택하기로 결정함.
|
|
- **Standard Webhooks**: Svix가 주도하는 `standard-webhooks` 사양은 `Webhook-Id`, `Webhook-Timestamp`, `Webhook-Signature` 헤더명을 권장하며, 이는 특정 벤더에 종속되지 않는 웹훅 표준의 기초가 됨.
|
|
|
|
## Related / 관련
|
|
|
|
- 관련 raw 자료: [[raw/official-docs/stripe-webhook-signature.md]], [[raw/official-docs/rfc9421-http-message-signatures.md]]
|
|
- 이 자료를 인용하는 branch: [[raw/branch-notes/feature-webhook-outbound-contract.md]]
|
|
- 인용하는 project: [[raw/project-notes/ca-skeleton-operational-contract.md]]
|