Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오. Node.js, Python, Go를 위한 완전한 웹훅 예시를 찾으십시오.

실용적인 웹 훅 예시: 보안 구현 안내서

Node.js, Python, Go를 위한 완전한 웹 훅 예시를 찾으세요. code를 사용하여 서명 인증, 재생 공격 방지, 엔드포인트 디버깅을 학습하세요.

마틴 도나디우

마틴 도나디우

콘텐츠 마케터

실용적인 웹 훅 예제: 보안 구현 안내서

서비스가 특정 이벤트가 발생했을 때 반응해야 하는 경우가 있습니다. 결제가 완료되거나 고객 정보가 변경되거나 저장소에 푸시가 발생하는 등. 매 분마다 API를 폴링하고

새로운 항목이 있는지 200와 같은 질문을 반복적으로 묻는 것보다, 이벤트가 발생했을 때 원본 시스템이 호출하는 것을 허용하는 것이 낫습니다.

대부분의 웹 훅 예제 기사에서는 여기서 멈칫합니다. 라우트를 보여주고 JSON 본문을 출력하고 반환하고, 그만합니다.

그 버전은 누군가가 위조된 요청을 보내거나 유효한 요청을 재생산하거나, 핸들러가 프레임워크가 본문을 해석하기 전에 서명 확인을 수행하지 않아 깨지기 전까지는 작동합니다.

What Are Webhooks and Why Use Them

Your billing provider marks an invoice as paid at 02:13. If your app learns about it at 02:14, the customer gets access right away. If your app learns about it on the next polling cycle, they wait, support gets a ticket, and your logs fill with avoidable noise. Webhooks solve that timing problem by sending an HTTP callback when the event happens.

In practical terms, a webhook is an event-driven POST from one system to another. The provider detects a change, such as payment, or repository activity, and sends the event data to a URL you control. That removes the constant “anything new yet?” loop that polling creates and cuts a lot of wasted requests. invoice.paid, order.createdThis pattern shows up in real systems because it maps cleanly to business events. Stripe posts payment outcomes. __CAPGO_KEEP_0__ posts repository activity. Shopify posts order updates. The shape is simple, but production behavior is not. A webhook that updates money, access, or inventory deserves the same care as any public __CAPGO_KEEP_1__ endpoint, especially once retries, duplicates, and untrusted traffic enter the picture. pushThe mental model that helps

This pattern shows up in real systems because it maps cleanly to business events. Stripe posts payment outcomes. GitHub posts repository activity. Shopify posts order updates. The shape is simple, but production behavior is not. A webhook that updates money, access, or inventory deserves the same care as any public API endpoint, especially once retries, duplicates, and untrusted traffic enter the picture.

원본 시스템

이벤트를 감지하는 서비스

  • 목적지 엔드포인트이벤트가 발생한 시점에 HTTP callback을 전송하여 타이밍 문제를 해결합니다.
  • Webhooks는 이벤트가 발생한 시점에 HTTP callback을 전송하여 타이밍 문제를 해결합니다.. __CAPGO_KEEP_0__ HTTP 경로에서 받은 데이터입니다.
  • 이벤트. 발생한 이름이 지정된 변경 사항, 예를 들어 invoice.paid 또는 push.
  • Payload. code가 필요한 세부 정보가 포함된 요청 본체입니다.

이벤트 제공자가 이미 발생한 사실에 대한 정보를 보내는 경우, __CAPGO_KEEP_0__의 역할은 보낸 사람을 확인하고 요청이 최신인지 확인한 후 변경 사항을 적용하는 것입니다. 마지막 부분은 많은 기본 튜토리얼에서 인정하지 않는다는 것보다 더 중요합니다. 실제 운영 환경에서 중복 전달은 일반적인 동작이며, corner case는 아닙니다.

실용적인 규칙: 이벤트 기반 업데이트를 위해 웹 훅을 사용하고, 예약된 읽기, 백필, 제공자가 외부 이벤트를 제공하지 않는 경우에는 폴링을 사용하십시오.

보다 광범위한 워크플로 자동화 및 데이터 통합을 구축하는 팀에서, 웹 훅은 시스템을 불필요한 요청 트래픽 없이 동기화하는 이벤트 층이 됩니다. 통합-heavy 서비스를 개발하는 경우 __CAPGO_KEEP_0__의, webhooks usually become the event layer that keeps systems in sync without unnecessary request traffic. If you work on integration-heavy services, Capgo’s 백엔드 개발 기사 이런 맥락이 유용한 이유는 코어 문제가 다시 시도, 큐, 관찰성, 그리고 실패 처리와 관련된 곳에서 나타난다.

운영 환경에서 성공하고 실패하는 것

운영 환경에서 잘 유지되는 설정은 보통 설계에서 흥미롭지 않다. 필요한 이벤트만 구독하라. 제공자나 이벤트 패밀리별로 엔드포인트를 범위로 하라. 이벤트 ID를 저장하여 중복 전달이 반복되는 부작용을 피하라. 요청이 검증되고 큐에 넣어질 때 빠른 2xx 응답을 반환하고, 그 다음 비즈니스 로직을 비동기적으로 수행하라.

약한 버전은 쉽게 식별된다. 하나의 일반 엔드포인트가 모든 것을 처리한다. 서명 확인이 초기 테스트 중에 건너뛰어지고 다시 돌아오지 않는다. 핸들러는 이벤트가 유효하거나陈舊인지 확인하기 전에 중요 테이블에 직접 쓰기 시작한다. 데모에서 작동하지만 다시 시도 폭탄, 제공자 장애, 또는 공격자가 이전 요청을 재생하는 경우에 실패한다.

이 가이드의 나머지 부분은 이 트레이드 오프로 정의된다. 웹후크 수신기

Before writing code, it helps to look at the request as raw HTTP instead of as a framework object. A typical webhook is just an HTTP POST to a public endpoint with headers and a JSON body.

버전은 작다. 운영 환경에 적합한 버전은 서명 확인, 재생 방어, 중복 처리, 그리고 디버깅 훅을 처음부터 추가한다.

POST /webhooks/orders HTTP/1.1
Host: your-app.example
Content-Type: application/json
User-Agent: Provider-Webhooks/1.0
X-Webhook-Signature: sha256=abc123example
X-Webhook-Timestamp: 1712345678

{
  "event": "order.created",
  "id": "evt_123",
  "data": {
    "order_id": "ord_456",
    "status": "created"
  }
}

웹후크 HTTP 요청의 구조

  • Method. In practice, webhook deliveries are usually POST requests.
  • Content-Type. 현대 대부분의 제공자는 JSON을 보냅니다.
  • User-Agent. 디버깅을 위해 도움이지만 신뢰를 위한 것은 절대 아닙니다.
  • Signature header. 제공자의 인증 확인을 담고 있습니다.
  • Timestamp header. 오래된 또는 재생된 요청을 거부하기 위해 사용됩니다.

몸체 형태가 왜 중요합니까

당신의 code는 일반적으로 모든 field에 관심이 없습니다. 이벤트 유형, 이벤트 식별자, 그리고 내부의 비즈니스 객체에만 관심이 있습니다. data. 그 이유는 좋은 핸들러는 필요한 것만 파싱하고 나머지는 디버깅을 위해 로깅합니다.

OpenAPI는 이 패턴을 직접 모델링합니다. OpenAPI 3.1.0은 최상위 수준의 웹훅 지원을 추가하여 첫 번째 클래스 웹훅 지원을 제공했습니다. webhooks 각 웹훅은 제공자에 의해 트리거되는 Path Item과 같은 형식으로 설명되는 객체입니다. canonical 예제는 웹훅, operation, JSON 요청 본문 및 수신 확인을 나타내는 응답을 포함합니다. OpenAPI 웹훅 예제를 참조하세요. newPet 자신의 수신자 또는 제공자 계약을 문서화하는 경우 강력한 예시가 추상 스키마 문장보다 더 도움이 됩니다. SheetMergy의 __CAPGO_KEEP_0__ 문서 예시를 참조하는 것처럼, 요청 예시, field 설명 및 예상 응답이 어떻게 함께 작동하는지 명확하게 나타낼 수 있습니다. post 웹훅은 전송层에서 간단합니다. 대부분의 실패는 헤더, 본문 인코딩 또는 서명 규칙에 대한 일치하지 않는 가정으로부터 발생합니다. 200 웹훅 서명 인증을 안전하게 nasıl 수행하나요? 서명된 웹훅은 한 가지 질문에 답합니다: 이 페이로드는 공유 비밀을 알고 있는 사람에게서 왔습니까?.

이것은 요청이 최근인지 또는 이미 처리했는지 여부를 묻는 것과 다릅니다. 서명 인증은 첫 번째 게이트가 아니라 마지막 게이트입니다. SheetMergy’s API doc examples 웹훅 서명 인증을 안전하게 수행하는 방법에 대한 자세한 내용은 Capgo 문서를 참조하세요.

웹훅 서명 인증을 안전하게 수행하는 방법에 대한 자세한 내용은 Capgo 문서를 참조하세요.

웹훅 서명 인증을 안전하게 수행하는 방법에 대한 자세한 내용은 Capgo 문서를 참조하세요.

웹훅 서명 인증을 안전하게 수행하는 방법에 대한 자세한 내용은 Capgo 문서를 참조하세요.

웹훅 서명 인증을 안전하게 수행하는 방법에 대한 자세한 내용은 Capgo 문서를 참조하세요.

__CAPGO_KEEP_0__

__CAPGO_KEEP_1__

__CAPGO_KEEP_2__

  1. __CAPGO_KEEP_3__
  2. __CAPGO_KEEP_4__ __CAPGO_KEEP_5__ __CAPGO_KEEP_6__
  3. __CAPGO_KEEP_7__
  4. __CAPGO_KEEP_8__
  5. __CAPGO_KEEP_9__
  6. __CAPGO_KEEP_10__

__CAPGO_KEEP_11__

What to watch for in real code

These are the mistakes I see most often:

  • JSON을 해싱하는 것은. 하지 마세요. JSON.stringify(req.body) 그것이 일치할 것으로 기대하지 마세요.
  • 일반 문자열 비교를 사용하는 것은. 타이밍에 안전한 비교를 사용하세요.
  • 비밀을 직접 하드 코딩하는 것은. 환경 변수나 비밀 관리 도구에 저장하세요.
  • 헤더만 신뢰하는 것은. 서명 헤더는만 검증을 통해 의미가 있습니다.

서비스 간 비밀 관리를 강화하는 팀에게는 Capgo의 비밀 관리 가이드가 있습니다. API 키 보안을 위한 앱 스토어 준수 __CAPGO_KEEP_0__ 키 보안이 관련이 있는 이유는 동일한 규칙이 여기에도 적용되기 때문이다. 비밀번호 회전, 범위 내 접근, 로그에서 누출을 피하는 것은 모두 웹훅 수신자에게도 중요하다.

일반적인 인증 예시

const crypto = require('crypto');

function verifySignature(rawBody, receivedSignature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  const a = Buffer.from(receivedSignature, 'utf8');
  const b = Buffer.from(expected, 'utf8');

  if (a.length !== b.length) return false;
  return crypto.timingSafeEqual(a, b);
}

이것은 의도적으로 일반적이다. 실제 제공자는 서명에 접두사를 붙인다, 시간을 조합하여 서명된 콘텐츠에 포함하거나, 해시를 다른 방식으로 인코딩한다. 규칙은 여전히 동일하다. 제공자의 정확한 서명 형식을 따르며, 항상 원본 페이로드와 일치하는지 확인한다.

재생 공격 방지

서명된 웹훅가 여전히 위험할 수 있다. 그것이 몇 시간 후에 도착하고 처리기에서 새로운 것으로 처리하는 경우가 더 자주 발생한다. 그들이 예상하는 것보다 더 자주 발생한다. 프록시가 로그 트래픽을 기록하거나, 요청 페이로드가 잘못된 곳으로 누출되거나, 제공자가 네트워크 오류 후에 다시 시도하고 엔드포인트가 동일한 이벤트를 두 번 처리하는 경우가 있다.

웹 애플리케이션에서 재생 공격을 효과적으로 방지하기 위한 5 가지 주요 보안 조치의 체크리스트를 보여주는 예시.

서명 인증은 다음 질문에 답한다: 공유 비밀번호로 이 페이로드를 생성한 사람인가? 재생 보호는 다른 질문에 답한다: 이 요청이 지금 여전히 받아들여져야 하는가? 실제 수신자는 둘 다 필요하다.

실제로 중요하는 최소한의 확인

실용적인 재생 방어는 서명된 타임스탬프로 시작된다. 제공자는 헤더나 서명된 메시지에 타임스탬프를 포함하고, 수신자는 작은 허용 창 밖의 요청을 거부한다.

이 흐름은 다음과 같이 보아야 한다.

  • 제공자가 정의한 위치에서 타임스탬프를 읽어라. 제공자 명세에 따라 정수 또는 RFC 형식의 날짜로 해석하십시오.
  • 서버 시간과 비교하십시오.이미 너무 오래된 요청 또는 너무 먼 미래의 요청을 거부하십시오.
  • 서명 방식에서 timestamp를 확인하십시오. 제공자가 timestamp를 지원할 때..
  • timestamp가 서명에 포함되지 않으면 공격자가 원본 본문을 교체하여 재생할 수 있습니다. 항상 제공자의 정확한 서명 형식을 확인하여 timestamp 논리를 신뢰하기 전에..
  • tolerance window를 선택하십시오. 5분은 일반적인 기본값입니다. 공격 창을 줄이기 위해 충분히 짧지만, 작은 시계 드리프트와 일반적인 네트워크 지연을 견딜 수 있습니다.

30초 창은 더 안전해 보이지만, 실제 시스템에서 다시 시도, 대기열, 지역 지연과 관련된 경우 더 자주 발생합니다. 반면 30분 창은 운영하기 더 쉬우나, 서명된 요청이 노출되면 공격자에게 더 많은 시간을 제공합니다. 몇 분으로 시작하여 서버를 NTP와 동기화한 후 제공자의 전달 패턴이 지원하는 경우에만 조정하십시오.

재생 방어는 단순히 timestamp 검사만 아닙니다.

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__

__CAPGO_KEEP_0__의 timestamp 유효성 검사로 인해 지속 시간이 만료된 요청을 차단하지 않습니다. 유효한 윈도우 내에서 중복 처리를 중단하지 않습니다. 만약 유효한 윈도우 내에서 동일한 서명된 이벤트가 두 번 전달된다면, 애플리케이션은 여전히 동일한 이벤트를 인식해야 합니다.

두 번째 층을 사용하세요:

  • 이벤트 ID 또는 전달 ID를 Redis와 같은 짧은 유효 시간 저장소에 저장하세요.
  • 처리 핸들러를 idempotent로 처리하세요. 중복된 주문, 이메일, 또는 청구 작업이 생성되지 않도록 반복적인 전달이 중복 처리되지 않도록 하세요.
  • 유효하지 않은 지속 시간이 만료된 요청을 로그하세요. 이유 코드와 함께 로그하세요. 그러나 비밀번호나 전체敏感 데이터를 로그하지 마세요.
  • 유효성 검사와 큐 작업을 나누어 처리하세요. 유효 시간 윈도우와 취소와 같은 팀은 패턴을 인식할 것입니다.

Capgo의 Capgo 앱에서 토큰 취소 패턴에 대한 Capgo의 안내서 Capacitor’s guide to 동일한 기능을 수행하는 아이디어를 다룹니다. 한 번 유효한 자격 증명이나 요청은 영원히 신뢰되지 않아야 합니다.

서명된 만료된 것은 여전히 안전하지 않습니다.

Node.js에서 웹후크 수신기를 구축하는 방법

Node와 Express를 사용하면 serious한 수신기를 온라인으로 빠르게 구축할 수 있지만, 다른 어떤 것보다 더 중요한 함정 하나가 있습니다. raw body에 접근할 수 있어야 합니다. Express가 객체로 변환하기 전에.

wooden 데스크 위에 있는 laptop이 Node.js 수신기 code를 VS Code 에디터 환경에서 보여주고 있습니다.

실제 Express 예제

const express = require('express');
const crypto = require('crypto');

const app = express();
const PORT = process.env.PORT || 3000;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;

// Capture raw body for signature verification
app.use(
  express.json({
    verify: (req, res, buf) => {
      req.rawBody = buf;
    },
  })
);

function safeEqual(a, b) {
  const aBuf = Buffer.from(a, 'utf8');
  const bBuf = Buffer.from(b, 'utf8');
  if (aBuf.length !== bBuf.length) return false;
  return crypto.timingSafeEqual(aBuf, bBuf);
}

function verifySignature(rawBody, secret, receivedSignature) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return safeEqual(expected, receivedSignature);
}

function isFresh(timestampHeader, toleranceSeconds = 300) {
  const timestamp = Number(timestampHeader);
  if (!Number.isFinite(timestamp)) return false;

  const now = Math.floor(Date.now() / 1000);
  return Math.abs(now - timestamp) <= toleranceSeconds;
}

app.post('/webhooks/example', async (req, res) => {
  const signature = req.get('x-webhook-signature');
  const timestamp = req.get('x-webhook-timestamp');

  if (!WEBHOOK_SECRET) {
    return res.status(500).send('Webhook secret is not configured');
  }

  if (!signature || !timestamp) {
    return res.status(400).send('Missing required security headers');
  }

  if (!isFresh(timestamp)) {
    return res.status(401).send('Stale webhook');
  }

  const valid = verifySignature(req.rawBody, WEBHOOK_SECRET, signature);
  if (!valid) {
    return res.status(401).send('Invalid signature');
  }

  // Acknowledge quickly
  res.status(200).send('OK');

  // Process after acknowledgement
  try {
    const event = req.body;
    console.log('Accepted event:', event.event, event.id);
    // enqueueJob(event)
  } catch (err) {
    console.error('Post-ack processing failed:', err);
  }
});

app.listen(PORT, () => {
  console.log(`Webhook receiver listening on ${PORT}`);
});

이 구조가 지속되는 이유

여기서 일부 선택은 의도적으로 선택한 것입니다.

  • 미들웨어에서 raw body를 캡처합니다.원래 바이트를 해싱하기 위해 보존합니다.
  • 업무 논리 전에 타임스탬프를 확인합니다.만료된 트래픽에 대한 작업을 하지 마십시오.
  • The route returns quickly. Long-running work belongs in a queue or background task. 200 빠르게 반환됩니다. 오랜 시간이 걸리는 작업은 큐 또는 백그라운드 작업에 속합니다.Post-ack processing is isolated. Even if downstream logic fails, the receiver path stays small.
  • post-ack 처리는 분리됩니다. 다운스트림 논리가 실패하더라도 수신자 경로는 작습니다.Secrets are the weak point in a lot of webhook implementations. Don’t keep them in source, don’t paste them into test fixtures, and don’t echo them in logs. If you need a broader process around rotation and CI handling, __CAPGO_KEEP_0__’s guide to managing secrets in CI/CD pipelines covers the operational side well.

비밀은 많은 웹후크 구현의 약점입니다. 소스에 저장하지 마세요, 테스트 fixture에 붙여넣지 마세요, 로그에 반영하지 마세요. rotation과 CI 처리에 대한 더 광범위한 프로세스가 필요하다면, Capgo의 CI/CD pipeline에서 비밀 관리하는 방법에 대한 지침은 운영 측면을 잘 다룹니다. A short walkthrough helps if you want to see the moving pieces in action: 짧은_walkthrough가 필요하다면, 움직이는 조각을 실제로 볼 수 있습니다.

What I’d change for a live system

실제 시스템에 대한 변경 사항

For a real provider integration, I’d add event ID deduplication in persistent storage, structured logs with request IDs, and a queue behind the acknowledgment path. I’d also avoid a single generic endpoint if multiple providers use different signature formats. Separate handlers are easier to reason about and harder to break.

실제 제공자 통합을 위해, 이벤트 ID 중복 제거를 영구 저장소에, 요청 ID가 포함된 구조화된 로그를, 그리고 확인 경로 뒤에 큐를 추가합니다. 또한 여러 제공자가 다른 서명 형식을 사용하는 경우, 단일 일반 엔드포인트를 피하고 별도의 핸들러를 사용합니다. 별도의 핸들러는 더 쉽게 이해하고 더 어려운 경우를 방지합니다. Building a Webhook Receiver in Python

Flask는 clean web hook 예제에 적합한 선택입니다. 요청 처리는 명확하고, Python 표준 라이브러리는 HMAC에 필요한 모든 것을 이미 제공합니다.

Node와 동일한 것을 기억해야 하는 주요 사항은 요청의 raw byte와 parsed JSON dict를 확인하는 것입니다.

서명 및 타임스탬프 확인을 포함한 Flask 예제

import os
import time
import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)
WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "")

def is_fresh(timestamp_header, tolerance_seconds=300):
    try:
        timestamp = int(timestamp_header)
    except (TypeError, ValueError):
        return False

    now = int(time.time())
    return abs(now - timestamp) <= tolerance_seconds

def verify_signature(raw_body, secret, received_signature):
    expected = hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected, received_signature)

@app.route("/webhooks/example", methods=["POST"])
def webhook():
    if not WEBHOOK_SECRET:
        return "Webhook secret is not configured", 500

    signature = request.headers.get("X-Webhook-Signature")
    timestamp = request.headers.get("X-Webhook-Timestamp")

    if not signature or not timestamp:
        return "Missing required security headers", 400

    if not is_fresh(timestamp):
        return "Stale webhook", 401

    raw_body = request.get_data()

    if not verify_signature(raw_body, WEBHOOK_SECRET, signature):
        return "Invalid signature", 401

    payload = request.get_json(silent=True) or {}

    # Acknowledge receipt
    response = jsonify({"status": "ok"})

    # In production, queue payload here instead of heavy sync work
    print("Accepted event:", payload.get("event"), payload.get("id"))

    return response, 200

if __name__ == "__main__":
    app.run(port=5000, debug=True)

Flask에 대한 구체적인 세부 사항

request.get_data() 이것이 가장 중요한 호출입니다. 요청 본문의 raw byte를 제공합니다. 바로 request.json로 이동하면 서명 불일치가 혼란스러워집니다.

몇 가지 구현 노트:

  • plain equality 대신 hmac.compare_digest missing 헤더를 클라이언트 실패로 처리하고
  • early reject plain equality 대신
  • missing 헤더를 클라이언트 실패로 처리하고 silent=True JSON 파싱을 위해 Flask가 오류를 발생시키기 보다는 오류 처리를 제어하기 위해.
  • 경로를 얇게 유지하세요파이로드가 비용이 많이 드는 것을 트리거하면 작업을 큐에 넣으세요.

보안 검증을 완화하여 서명 불일치로 디버깅하지 마십시오. 서명 불일치로 디버깅하기 위해, 정확히 해시한 바이트를 출력하고, 제공자가 기대하는 서명 형식을 출력하세요.

팀들이 일반적으로 막히는 곳

일반적인 실패 경로는 JSON 본체를 수동으로 빌드하여 테스트한 다음, 실제 제공자와 서명이 일치하지 않는다는 것을 발견하는 것입니다. 그 일반적으로 다음 중 하나가 의미를 갖습니다: 제공자는 시간이 지남에 따라 봉투를 서명합니다, 서명이 당신이 가정한 것과 다르게 인코딩되어 있습니다, 또는 미들웨어가 본체를 검증하기 전에 본체를 변경했습니다.

그럴 때, 중간에 crypto code를 임의로 변경하지 마십시오. 원본 헤더와 원본 본체를 캡처하고, 작은 고립된 스크립트에서 해시를 재생산한 다음, Flask 경로에 다시 넣으세요.

Go로 웹후크 수신기를 구축하는 방법

Go는 웹후크 수신기에 좋은 선택입니다. 표준 라이브러리가 충분하기 때문입니다. 프레임워크가 필요하지 않으며 code를 쉽게 진실하게 유지할 수 있습니다.

주의할 것은 본체 처리입니다. r.Body 본체는 스트림입니다. 한 번 읽고, 얻은 바이트를 해시하고, 그 동일한 바이트에서 언마샬링하세요.

A 표준 라이브러리 예시

package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"
)

type WebhookPayload struct {
	Event string          `json:"event"`
	ID    string          `json:"id"`
	Data  json.RawMessage `json:"data"`
}

func isFresh(timestampHeader string, toleranceSeconds int64) bool {
	ts, err := strconv.ParseInt(timestampHeader, 10, 64)
	if err != nil {
		return false
	}

	now := time.Now().Unix()
	diff := now - ts
	if diff < 0 {
		diff = -diff
	}

	return diff <= toleranceSeconds
}

func verifySignature(rawBody []byte, secret string, received string) bool {
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write(rawBody)
	expected := hex.EncodeToString(mac.Sum(nil))

	if len(expected) != len(received) {
		return false
	}

	return subtle.ConstantTimeCompare([]byte(expected), []byte(received)) == 1
}

func webhookHandler(secret string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
			return
		}

		signature := r.Header.Get("X-Webhook-Signature")
		timestamp := r.Header.Get("X-Webhook-Timestamp")

		if signature == "" || timestamp == "" {
			http.Error(w, "missing required security headers", http.StatusBadRequest)
			return
		}

		if !isFresh(timestamp, 300) {
			http.Error(w, "stale webhook", http.StatusUnauthorized)
			return
		}

		rawBody, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "failed to read body", http.StatusBadRequest)
			return
		}

		if !verifySignature(rawBody, secret, signature) {
			http.Error(w, "invalid signature", http.StatusUnauthorized)
			return
		}

		var payload WebhookPayload
		if err := json.Unmarshal(rawBody, &payload); err != nil {
			http.Error(w, "invalid json", http.StatusBadRequest)
			return
		}

		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))

		log.Printf("accepted event=%s id=%s", payload.Event, payload.ID)
	}
}

func main() {
	secret := os.Getenv("WEBHOOK_SECRET")
	if secret == "" {
		log.Fatal("WEBHOOK_SECRET is not set")
	}

	http.HandleFunc("/webhooks/example", webhookHandler(secret))

	log.Println("listening on :8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Go가 여기서 견고하게 느껴지는 이유

몇 가지 이점이 눈에 띄고 있습니다:

  • 핸들러는 명시적입니다. 숨겨진 미들웨어 마법이 없습니다.
  • 엣지에서 타이핑이 도움이 됩니다. 헤더 파싱, 타임스탬프 변환, JSON 디코딩 모두 명확하게 실패합니다.
  • 표준 암호화 패키지가 충분합니다. 기본 HMAC 검증을 위한 추가 종속성이 필요하지 않습니다.

운영 노트

웹훅 볼륨이 증가하면 Go의 동시성 모델은 HTTP 진입점을 변경하지 않고 배경 작업을 확장할 수 있는 공간을 제공합니다. 그 때도 수신자 너비를 유지하세요. 수락, 유효성 검사, 확인, 그리고 응답이 돌아오기 전에 작업을 넘겨주세요.

가장 강력한 Go 웹훅 핸들러 중에 가장 단순한 것들이 가장 강력합니다. transport 검증을 비즈니스 로직과 섞지 않고, 응답이 돌아오기 전에 데이터베이스 작업을 수행하지 않습니다.

기본 디버깅 기법

웹후크 버그는 일반적으로 스택 트레이스 대신 지원 메시지로 나타납니다. 제공자는 이벤트를 전달했다고 말합니다. 엔드포인트는 앱에 도달한 것이 없거나, 첫 눈에 유효한 요청이지만 서명 검증이 실패했다고 말합니다. 그 시점에서 디버깅은 정확한 HTTP 교환을 재구성하고, 바이트 단위로, 그리고 그것이 어디서 깨졌는지 증명하는 것입니다.

소프트웨어 개발 환경에서 웹후크 디버깅을 위한 필수 도구와 기법의 목록입니다.

실용적인 디버깅 도구킷

전선 형식부터 시작하세요.

서명 검증이 실패하면, 받은 원본 요청 본문과 검증에 사용된 헤더를 캡처하세요. 실제로 버그는 종종 지루합니다. 프레임워크는 JSON을 해싱하기 전에 파싱했으며, 프록시가 인코딩을 변경했거나, 테스트 재생이 원래 타임스탬프 헤더를 놓쳤습니다. 로깅된 객체는 충분하지 않습니다. 원본 바이트와 검증 입력이 필요합니다.

이 도구들은 문제를 빠르게 분리하는 데 도움이 됩니다:

  • 원본 요청 캡처. 조사 중에는 헤더, 콘텐츠 유형, 콘텐츠 길이, 그리고 수정되지 않은 본문을 로깅하세요.
  • 요청 검사 엔드포인트. 서비스들처럼 webhook.site 보내는 쪽이 전송한 것을 확인하는 데 도움이 됩니다.
  • Local tunneling. ngrok 및 같은 도구는 로컬 수신자와 함께 테스트 할 수 있게 해주며 제공자도 루프에 유지합니다.
  • 수동 재생. __CAPGO_KEEP_0__ 또는 제공자 페이로드가 문제인지 확인하기 위해 가장 빠른 방법은 Postman과 같은 동일한 본체와 헤더로 요청을 다시 빌드하는 것입니다. curl or Postman using the same body and headers. That is the quickest way to confirm whether your code or the provider payload is the issue.
  • . 보낸 사람 대시보드에는 종종 응답 코드, 재시도 기록 및 요청 식별자가 로그와 일치시킬 수 있는 로그가 포함되어 있습니다.패턴은 중요합니다. 외부에서 내부로 작업하세요. 먼저 제공자가 예상한 것과 같은 것을 보냈는지 확인하세요. 그런 다음 서버가 동일한 바이트를 받았는지 확인하세요. 마지막으로 __CAPGO_KEEP_0__가 동일한 바이트와 동일한 비밀번호와 타임스탬프 규칙으로 동일한 해시를 생성했는지 확인하세요.

The pattern matters. Work from the outside in. First verify the provider sent what you expected. Then verify your server received the same bytes. Then verify your code hashed the same bytes with the same secret and timestamp rules.

좋은 웹후크 로그는 한 번의 검색으로 세 가지 질문에 답해야 합니다.

질문

유용한 로그 필드__CAPGO_KEEP_0__
요청이 도착했나요?route, method, received_at
왜 거부되었습니다?missing_header, stale_timestamp, signature_failed
이후에 연관지을 수 있나요?event_id, provider_request_id

실제 시스템에서는 네 번째 필드가 도움이 됩니다. 로컬로 추가하여 request_id 받는 쪽에서 생성하여 요청을 따라 추적할 수 있도록 해주세요.

저장할 때 선택적이세요. 비밀을 로깅하지 마세요. 고객 데이터, 접근 토큰, 청구 세부 정보가 포함된 전체 프로덕션 페이로드를 덤프하지 마세요. 고객 데이터, 접근 토큰, 청구 세부 정보가 포함된 전체 프로덕션 페이로드를 덤프하지 마세요. 대신 메타데이터와 짧은 페이로드 해시를 로깅하세요. 그럼에도 불구하고 재시도와 두 번의 전달이 동일한지 확인할 수 있습니다.

원래 입력으로 실패한 요청을 재생산하세요.

기본 튜토리얼은 이 부분을 생략합니다. 실패한 요청을 정확히 재생산할 수 없다면 추측하는 것입니다.

실패한 웹후크를 다음과 같이 저장하세요:

  • raw body bytes
  • 모든 서명 관련 헤더
  • 요청 시간
  • 콘텐츠 유형
  • 제공자 요청 ID

그런 다음 스테이징 엔드포인트에 다시 재생합니다. 재생이 통과하면 전송 중에 변경된 것을 비교합니다. 일반적인 범인에는 요청 본문이 정규화되는 미들웨어, 문자 인코딩 불일치, 헤더를 제거하거나 다시 쓰는 로드 밸런서가 있습니다. 또한 팀이 실제 요청 본문을 대신하여 예쁘게 인쇄된 대시보드 뷰에서 페이로드를 복사하는 경우도 실패를 유발합니다. whitespace 차이 alone이 HMAC 검증을 깨뜨릴 수 있습니다.

더 광범위한 릴리스 및 모바일 전송 문제 해결을위한 Capgo의 가이드 Capacitor의 OTA 업데이트를 디버깅하는 데 사용되는 Capacitor다른 전송, 동일한 교훈. 실제 요청 경로를 변경하기 전에 애플리케이션 code를 캡처하십시오.

서명 검증이 실패하면, 실제 바이트, 검증에 사용된 정확한 헤더 및 시간 스탬프 값을 만지지 않고 암호화 code를 검사하십시오.

생산 준비가 된 웹후크의 체크리스트

웹후크 핸들러는 스테이징에서 처음으로 재시도 폭풍, 잘못된 페이로드 또는 서명 불일치가 발생할 때까지 정상적으로 보입니다. 2시. 생산 단계의 바는 더 높습니다. 수신자는 위조된 요청을 거부하고 유효한 재시도를 수락하며 실패를 디버깅하는 데 필요한 신호를 제공하는 동시에敏感 데이터를 공개하지 않도록 해야합니다.

보안 및 정확성 검사

  • 모든 요청 서명 확인. URL이 노출됩니다. 테스트 URL이 채팅에서 공유됩니다. 서명 확인은 공유 비밀을 알고 있는 송신자가 알려주는 제어입니다.
  • 기존 요청 거부. 유효한 서명이 오래된 데이터 패킷에도 적용될 수 있습니다. 제공자의 재시도 모델과 일치하는 타임스탬프 허용 범위를 설정하세요.
  • JSON이 아닌 raw body를 해시하세요. 미들웨어는 키 순서를 변경하거나 공백을 정규화하거나 인코딩을 변경할 수 있습니다. 검증은 실제로 도착한 바이트와 정확히 일치해야 합니다.
  • code에 서명 비밀을 유지하세요. 환경 변수는 기본입니다. 자주 회전하거나 여러 환경에서 실행하는 경우 비밀 관리기를 사용하는 것이 더 적합합니다.
  • 인증 오류 시 실패. 서명 헤더가 누락되거나 오류가 발생하거나 예상치 못한 스키마를 사용하는 경우 요청을 거부하고 이유를 로깅하세요.

신뢰성 검사

  • 빠른 승인. 제공자들은 일반적으로 2xx를 성공으로 처리하므로 요청을 검증하고 필요한 것을 영구 저장하고 느린 작업을 큐 또는 워커로 이동하세요.
  • 처리 핸들러를 무결성 처리하라. 동일한 이벤트가 여러 번 도착할 수 있으므로 이벤트 ID, 전달 ID 또는 안정적인 제공자 식별자를 사용하여 키드 사이드 이펙트를 분리하라.
  • 예측 가능한 오류 코드를 반환하라. 잘못된 입력을 위해 400 를 사용하고 401 로 실패한 검증을 위해 403 를 사용하고 5xx 는 시스템이 문제가 될 때만 사용하라. 이는 제공자 재시도 동작을 더 쉽게 이해할 수 있도록 한다.
  • 파싱하기 전에 제한을 설정하라. 요청 크기, 콘텐츠 유형 및 헤더 수를 조기 설정하여 웹후크 엔드포인트가 일반적인 인식 구멍으로 변하지 않도록 한다.
  • __CAPGO_KEEP_0__을 유지하세요. API의 필드와 이벤트 타입만 받아들이세요. 느슨한 파싱은 처음에는 편리하게 느껴지지만 API 변경 시 비용이 많이 들게 됩니다.

관찰성 검사

좋은 웹후크 연산은 흥미롭지 않습니다. 팀은 빠르게 세 가지 질문에 답할 수 있습니다: 받았나요? 검증했나요? 다운스트림 처리가 성공했나요?

그 표준을 사용하세요.

  • 받은 것을 추적하세요, 검증을 추적하세요, 처리를 추적하세요. 각각의 결과를 별도로 추적하세요..
  • 요청 ID, 이벤트 ID, 서명 상태, 타임스탬프 왜곡을 로깅하세요..
  • 대기열 지연, 핸들러 지연, 재시도 볼륨을 측정하세요..
  • __CAPGO_KEEP_0__을 안전하게 재생할 수 있는 경로를 유지하세요. 단계 또는 재배포 워크플로우에 사용하세요..
  • 패턴 변경에 대한 경고를 설정하세요. 서명 실패의 급증 또는 중복 배달과 같은 경우를 예로 들 수 있습니다.__CAPGO_KEEP_0__은 더 넓은 운영적 지점의 유익한 예시입니다. 업데이트 워크플로우에 배포 관련 도구 및 관찰성 도구를 포함하고 있으며, 일부 생태계는 웹후크 관련 흐름과도 관련이 있습니다. 이 교훈은 실제입니다. 배달 시스템은 수신부터 완료까지의 시각성을 필요로 합니다.

Capgo

프로덕션 환경에서 잘 작동하려면 팀이 위의 체크를 모두 완료해야 합니다. 만약 체크 중 하나가 누락되어 있다면, 그 빈틈은 인시던트가 발생하는 동안 보이지 않고 데모 시에 나타납니다.

웹훅에 대한 자주 묻는 질문

어떤 상태 code를 반환해야 하나요?

Return a 200 웹훅을 수락한 경우에 대해 처리합니다. 유효성 검사 실패 시, 실패와 일치하는 클라이언트 오류 또는 인증 오류를 반환합니다. 400 for 오류가 있는 입력 또는 401 인증 정보가 유효하지 않은 경우입니다. 그 로직을 유지하여 제공자 대시보드가 더 쉽게 해석되도록 하세요.

웹훅 처리를 동기적으로 진행해야 하나요?

일반적으로 그렇지 않습니다. 유효성을 검사하고 이를 인지한 다음 실제 작업을 큐 또는 백그라운드 워커로 밀어 넣습니다. 이 방식은 전달 경로를 빠르게 유지하고 다운스트림 처리가 느려지면 중복된 재시도가 발생하는 것을 방지합니다.

재시도는 어떻게 처리해야 하나요?

이벤트 ID나 제공자 전달 ID를 사용하여 동일한 이벤트를 받았을 때 중복된 부작용이 발생하지 않도록 핸들러에 무결성 특성을 구축하세요.

What if events arrive out of order?

처리 순서에 구애받지 않는 핸들러를 설계할 수 있는 경우에만 가능합니다. 만약 비즈니스 프로세스가 순서가 필요하다면, 이벤트 순서가 전달 순서를 반영하는 것으로 가정하지 말고, 대신에陈舊한 전환을 감지하기 위해 충분한 상태를 저장하세요.

웹훅 버전 변경을 어떻게 처리할까요?

버전을 명시적으로 지정하여 핸들러 논리를 관리하세요. 제공자에 따라 특정한 파싱을 분리하고, 코드베이스에 payload 가정치를 흩어놓지 말고, 새로운 형식에 대한 지원을 출시하기 전에 실제 캡처된 샘플과 함께 테스트를 추가하세요.


팀이 Capacitor 또는 Electron 앱을 배포한다면 Capgo 웹훅 디자인의 동일한 엔지니어링 인стинктив에 맞춰, 입력을 검증하고, 릴리스 경로를 관찰하고, 장애를 빠르게 복구할 수 있는 팀에게는 __CAPGO_KEEP_0__가 유용합니다. signed web updates를 전달하고, 롤아웃 동작을 관찰하고, 앱 스토어 리뷰를 기다리지 않고 장애를 복구할 수 있습니다.

Capacitor 앱의 실시간 업데이트

웹-layer 버그가 활성화된 경우, Capgo을 통해 픽스를 배포하는 것이 앱 스토어 승인까지 기다리는 것보다 더 빠릅니다. 사용자는 배경에서 업데이트를 받으면서 네이티브 변경은 일반적인 검토 경로를 유지합니다.

시작하기

블로그에서 최신 뉴스

Capgo은 전문적인 모바일 앱을 만들기 위해 필요한 최고의洞察력을 제공합니다.