1. 웹훅 URL 설정
VoxAI는 두 곳에서 웹훅 URL을 설정할 수 있습니다.
| 레벨 | 설정 위치 | 용도 |
|---|
| Organization | 설정 > 웹훅 | 조직 전체 기본 웹훅 URL |
| Agent | 에이전트 대시보드 > 웹훅 설정 탭 | 에이전트별 개별 웹훅 URL |
Agent 웹훅 URL이 설정된 경우 해당 URL이 우선 적용됩니다.
미설정 시 Organization 웹훅 URL로 자동 fallback됩니다.
Transfer Agent로 전환되더라도 최초 인입 Agent의 웹훅 URL이 사용됩니다.
2. 보안
VoxAI는 IP 화이트리스트와 HMAC-SHA256 서명 두 가지 보안 레이어를 제공합니다.
IP 화이트리스트
VoxAI 웹훅 요청은 고정 IP 34.146.189.242에서 전송됩니다.
방화벽 또는 리버스 프록시에서 이 IP만 허용하면 외부 요청을 차단할 수 있습니다.
HMAC-SHA256 서명
HMAC 서명이 활성화되면, VoxAI는 모든 웹훅 요청에 두 개의 인증 헤더를 포함합니다.
| 헤더 | 설명 | 예시 |
|---|
X-Webhook-Timestamp | 서명 생성 시점 (Unix, 초) | 1738900000 |
X-Webhook-Signature | HMAC-SHA256 서명 | sha256=a1b2c3... |
서명은 아래와 같이 생성됩니다:
signed_payload = "{timestamp}.{normalized_json}"
signature = HMAC-SHA256(웹훅_서명_키, signed_payload)
normalized_json은 키 정렬 + 공백 없는 JSON입니다.
Python: json.dumps(payload, sort_keys=True, separators=(",", ":"))
JavaScript: JSON.stringify(sortKeys(payload)) (재귀적 키 정렬 필요)
웹훅 서명 키 관리
대시보드의 설정 > 웹훅 메뉴에서 웹훅 서명 키를 생성할 수 있습니다.
키 생성
설정 > 웹훅 페이지에서 키 발급 버튼을 클릭합니다.
키 복사 및 저장
생성된 키를 복사하여 서버의 환경 변수에 저장합니다.# .env
VOX_WEBHOOK_KEY=a3f7c9d2e5b8a1f4c6d9e2b5a8f1c4d7e0b3a6f9c2d5e8b1a4f7c0d3e6b9a2f5
웹훅 서명 키는 생성 직후 한 번만 전체 값이 표시됩니다.
이후에는 마지막 4자리만 확인할 수 있으므로, 반드시 안전한 곳에 저장하세요.
서명 키 재발급 — 기존 키 삭제 후 새 키를 생성합니다. 기존 키로 서명된 웹훅은 즉시 검증 실패합니다.
서명 키 삭제 — HMAC 서명이 비활성화되고, 웹훅 요청에 서명 헤더가 포함되지 않습니다.
3. 서버 구현
HMAC 서명 검증이 포함된 웹훅 엔드포인트 예제입니다.
import hmac, hashlib, json, time, os
from fastapi import FastAPI, Request, HTTPException
app = FastAPI()
WEBHOOK_KEY = os.environ["VOX_WEBHOOK_KEY"]
TOLERANCE = 5 * 60 # 5분
def verify_webhook(body: bytes, timestamp: str, signature: str, secret: str) -> bool:
if abs(time.time() - int(timestamp)) > TOLERANCE:
return False
normalized = json.dumps(json.loads(body), sort_keys=True, separators=(",", ":"))
expected = hmac.new(
secret.encode(), f"{timestamp}.{normalized}".encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature.removeprefix("sha256="))
@app.post("/webhook")
async def handle_webhook(request: Request):
body = await request.body()
ts = request.headers.get("X-Webhook-Timestamp", "")
sig = request.headers.get("X-Webhook-Signature", "")
if not verify_webhook(body, ts, sig, WEBHOOK_KEY):
raise HTTPException(status_code=401)
data = json.loads(body)
# 페이로드 처리 ...
4. 보안 권장사항
두 레이어 모두 사용
IP 화이트리스트 + HMAC 서명을 함께 사용하면 네트워크와 애플리케이션 레벨 모두에서 보안을 확보할 수 있습니다.
Timestamp 검증
타임스탬프가 현재 시간과 5분 이내인지 확인하여 Replay 공격을 방지합니다.
Timing-safe 비교
hmac.compare_digest (Python) 또는 crypto.timingSafeEqual (Node.js)로 Timing 공격을 방지합니다.
키 안전 보관
웹훅 서명 키는 환경 변수 또는 시크릿 매니저에 저장하고, 코드에 하드코딩하지 않습니다.