Documentation
¶
Overview ¶
Package reqsign provides HMAC-SHA256 request signing and verification for service-to-service authentication and webhook delivery.
Signing model ¶
The signed payload is:
<unix-timestamp-seconds>\n<request-body>
The HMAC-SHA256 of this payload (keyed with the shared secret) is transmitted in the request header as a hex string. The timestamp binds the signature to a specific point in time; the verifier rejects signatures outside a configurable replay window (default ±5 minutes).
This is the same scheme used by Stripe, GitHub, and Twilio webhooks.
Outbound (signing) ¶
transport := reqsign.NewSigningTransport([]byte("shared-secret"), nil)
client := &http.Client{Transport: transport}
client.Post(url, "application/json", body)
Inbound (verification) ¶
mux.Handle("/webhook", reqsign.Middleware([]byte("shared-secret"))(handler))
Index ¶
- Constants
- Variables
- func Middleware(secret []byte, opts ...VerifyOptions) func(http.Handler) http.Handler
- func Sign(secret, body []byte, timestamp time.Time) string
- func Verify(secret, body []byte, timestamp time.Time, sig string) bool
- func VerifyRaw(secret, body []byte, sig string) bool
- type SigningTransport
- type VerifyOptions
Constants ¶
const ( // DefaultHeader is the request header carrying the signature. DefaultHeader = "X-Signature" // DefaultTimestampHeader carries the Unix timestamp used in the signature. DefaultTimestampHeader = "X-Timestamp" // DefaultReplayWindow is the maximum age of an accepted signature. DefaultReplayWindow = 5 * time.Minute )
Variables ¶
var ErrInvalidSignature = errors.New("reqsign: invalid signature")
ErrInvalidSignature is returned when a request signature does not match.
var ErrMissingHeader = errors.New("reqsign: missing signature header")
ErrMissingHeader is returned when the expected signature headers are absent.
var ErrReplayDetected = errors.New("reqsign: timestamp outside replay window")
ErrReplayDetected is returned when the request timestamp is outside the replay window, indicating a potential replay attack.
Functions ¶
func Middleware ¶
Middleware returns an HTTP middleware that verifies the HMAC-SHA256 signature on inbound requests (e.g. webhooks from a third-party service).
The request body is read, verified, and then restored so that downstream handlers can read it again.
func Sign ¶
Sign computes an HMAC-SHA256 signature over "<timestamp>\n<body>" using the given secret. Returns the hex-encoded signature.
func Verify ¶
Verify checks whether sig is the correct HMAC-SHA256 signature of body at the given timestamp. Uses constant-time comparison to prevent timing attacks.
func VerifyRaw ¶ added in v1.0.1
VerifyRaw checks an HMAC-SHA256 signature over the raw body only, with no timestamp prefix. Use this for webhook providers that sign the body directly without a timestamp (e.g. Didit). Replay protection (timestamp window check) must be handled separately by the caller.
Uses constant-time comparison to prevent timing attacks.
ts, _ := strconv.ParseInt(r.Header.Get("X-Webhook-Timestamp"), 10, 64)
if math.Abs(float64(time.Now().Unix()-ts)) > 300 { /* reject */ }
ok := reqsign.VerifyRaw([]byte(secret), body, r.Header.Get("X-Webhook-Signature"))
Types ¶
type SigningTransport ¶
type SigningTransport struct {
Secret []byte
Transport http.RoundTripper
Header string
TimestampHeader string
}
SigningTransport is an http.RoundTripper that automatically signs every outbound request with HMAC-SHA256 before sending it.
client := &http.Client{Transport: reqsign.NewSigningTransport(secret, nil)}
func NewSigningTransport ¶
func NewSigningTransport(secret []byte, base http.RoundTripper) *SigningTransport
NewSigningTransport creates a SigningTransport with the given secret. base may be nil, in which case http.DefaultTransport is used.
type VerifyOptions ¶
type VerifyOptions struct {
// Header is the request header carrying the hex signature.
// Defaults to DefaultHeader ("X-Signature").
Header string
// TimestampHeader is the request header carrying the Unix timestamp.
// Defaults to DefaultTimestampHeader ("X-Timestamp").
TimestampHeader string
// ReplayWindow is how far in the past or future a timestamp may be.
// Defaults to DefaultReplayWindow (5 minutes).
ReplayWindow time.Duration
// OnError is called when verification fails. Defaults to a 401 JSON response.
OnError func(w http.ResponseWriter, r *http.Request, err error)
}
VerifyOptions configures the inbound verification middleware.