Documentation
¶
Overview ¶
Package webhook verifies signed incoming webhooks (HMAC-SHA256 over the raw body), the scheme used by GitHub, Shopify, Resend/Svix-style providers and your own integrations.
r.Post("/webhooks/github", handle).Use(webhook.Middleware("X-Hub-Signature-256", secret, 1<<20))
Middleware bounds the body, verifies the signature header and answers 401 (ErrSignature) on mismatch; VerifyHMAC and Sign are the building blocks for custom schemes and tests.
See https://github.com/AlexGalhardo/vukem/blob/main/docs/security.md.
Example ¶
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"strings"
"github.com/AlexGalhardo/vukem/webhook"
)
func main() {
secret := []byte("whsec_example")
handler := webhook.Middleware("X-Hub-Signature-256", secret, 1<<20)(
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
fmt.Fprintf(w, "accepted %s", body)
}),
)
payload := `{"action":"opened"}`
req := httptest.NewRequest(http.MethodPost, "/webhooks/github", strings.NewReader(payload))
req.Header.Set("X-Hub-Signature-256", webhook.Sign(secret, []byte(payload)))
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, req)
fmt.Println(rec.Code, rec.Body.String())
forged := httptest.NewRequest(http.MethodPost, "/webhooks/github", strings.NewReader(payload))
forged.Header.Set("X-Hub-Signature-256", "sha256=00")
rec = httptest.NewRecorder()
handler.ServeHTTP(rec, forged)
fmt.Println(rec.Code)
}
Output: 200 accepted {"action":"opened"} 401
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrSignature = &errs.HTTPError{Status: http.StatusUnauthorized, Message: "Invalid webhook signature."}
ErrSignature is the 401 answered for missing or wrong signatures.
Functions ¶
func Middleware ¶
Middleware verifies the signature in header against the raw body (at most maxBody bytes) and restores the body for the handler. Failures get 401 and a "webhook.signature_invalid" security event.
func VerifyHMAC ¶
VerifyHMAC reports whether signature is the HMAC-SHA256 of payload with secret. The signature may be hex or base64 and may carry a "sha256=" prefix. The comparison is constant time.
Example ¶
package main
import (
"fmt"
"github.com/AlexGalhardo/vukem/webhook"
)
func main() {
secret := []byte("whsec_example")
sig := webhook.Sign(secret, []byte("hello"))
fmt.Println(webhook.VerifyHMAC(secret, []byte("hello"), sig))
fmt.Println(webhook.VerifyHMAC(secret, []byte("hello!"), sig))
}
Output: true false
Types ¶
This section is empty.