Skip to main content

Overview

The signature middleware verifies request signatures using HMAC or other algorithms, ensuring requests haven’t been tampered with and come from authorized sources. Use it when you need:
  • Webhook signature verification
  • API request authentication
  • Tamper detection

Installation

import "github.com/go-mizu/mizu/middlewares/signature"

Quick Start

app := mizu.New()

// Verify HMAC-SHA256 signatures
app.Use(signature.New(signature.Options{
    Secret: []byte("your-secret-key"),
    Header: "X-Signature",
}))

Configuration

Options

OptionTypeDefaultDescription
Secret[]byteRequiredSigning secret
Headerstring"X-Signature"Signature header name
Algorithmstring"sha256"Hash algorithm
Encodingstring"hex"Signature encoding
TimestampHeaderstring""Timestamp header
TimestampTolerancetime.Duration5mMax timestamp age
GetSecretfunc(*mizu.Ctx) []byte-Dynamic secret lookup

Examples

Basic HMAC Verification

app.Use(signature.New(signature.Options{
    Secret: []byte("webhook-secret"),
    Header: "X-Signature",
}))

GitHub Webhook

app.Post("/webhook/github", handler, signature.New(signature.Options{
    Secret:   []byte(os.Getenv("GITHUB_WEBHOOK_SECRET")),
    Header:   "X-Hub-Signature-256",
    Prefix:   "sha256=",
    Algorithm: "sha256",
}))

Stripe Webhook

app.Post("/webhook/stripe", handler, signature.New(signature.Options{
    Secret:           []byte(os.Getenv("STRIPE_WEBHOOK_SECRET")),
    Header:           "Stripe-Signature",
    TimestampHeader:  "Stripe-Signature",
    ParseStripeStyle: true,
}))

With Timestamp Validation

app.Use(signature.New(signature.Options{
    Secret:             []byte("api-secret"),
    Header:             "X-Signature",
    TimestampHeader:    "X-Timestamp",
    TimestampTolerance: 5 * time.Minute,
}))

Per-Client Secrets

app.Use(signature.New(signature.Options{
    Header: "X-Signature",
    GetSecret: func(c *mizu.Ctx) []byte {
        clientID := c.Request().Header.Get("X-Client-ID")
        return getClientSecret(clientID)
    },
}))

Custom Signature Format

app.Use(signature.New(signature.Options{
    Secret:    secret,
    Header:    "Authorization",
    Prefix:    "HMAC-SHA256 ",
    Algorithm: "sha256",
    Encoding:  "base64",
}))

API Reference

Functions

// New creates signature verification middleware
func New(opts Options) mizu.Middleware

// Sign generates signature for data
func Sign(data, secret []byte, algorithm string) string

// Verify checks if signature is valid
func Verify(data, secret []byte, signature, algorithm string) bool

Signature Algorithms

AlgorithmDescription
sha256HMAC-SHA256 (recommended)
sha512HMAC-SHA512
sha1HMAC-SHA1 (legacy)

Creating Signatures (Client Side)

// Go client
func signRequest(body []byte, secret string) string {
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write(body)
    return hex.EncodeToString(mac.Sum(nil))
}
// JavaScript client
async function signRequest(body, secret) {
    const encoder = new TextEncoder();
    const key = await crypto.subtle.importKey(
        'raw', encoder.encode(secret),
        { name: 'HMAC', hash: 'SHA-256' },
        false, ['sign']
    );
    const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(body));
    return Array.from(new Uint8Array(sig))
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
}

Technical Details

Implementation Architecture

The signature middleware implements HMAC-based request verification using Go’s crypto packages:
  • Algorithm Support: SHA-1 (legacy), SHA-256 (default), SHA-512
  • Encoding Formats: Hexadecimal (default) and Base64
  • Signature Verification: Constant-time comparison using hmac.Equal() to prevent timing attacks

Core Components

Middleware Flow

  1. Path & Method Filtering: Checks if request should skip validation based on configured paths and methods
  2. Signature Extraction: Retrieves signature from configured header and validates prefix if specified
  3. Payload Reading: Uses default body reader or custom PayloadGetter function
  4. HMAC Verification: Computes expected signature and performs constant-time comparison
  5. Context Storage: Stores verification result in request context for downstream handlers

Key Functions

  • New(secret): Creates middleware with default options (SHA-256, hex encoding, X-Signature header)
  • WithOptions(opts): Creates middleware with custom configuration
  • Sign(secret, algo, encoding, payload): Generates HMAC signature for given payload
  • GetInfo(c): Retrieves signature verification information from context
  • IsValid(c): Quick check if signature was valid

Provider-Specific Helpers

The middleware includes pre-configured functions for popular webhook providers:
  • GitHub: X-Hub-Signature-256 header with sha256= prefix
  • Stripe: Stripe-Signature header (simplified implementation)
  • Slack: Custom payload format combining version, timestamp, and body
  • Twilio: SHA-1 with Base64 encoding, URL + sorted form parameters
  • AWS: Signature Version 4 (simplified implementation)

Security Features

  • Constant-Time Comparison: Uses hmac.Equal() to prevent timing attacks
  • Body Preservation: Re-wraps request body after reading for downstream handlers
  • Configurable Skip Rules: Allows excluding specific paths and HTTP methods
  • Custom Error Handling: Supports custom error responses via ErrorHandler

Best Practices

  • Use SHA-256 or stronger algorithms
  • Include timestamp to prevent replay attacks
  • Use secure random secrets (32+ bytes)
  • Rotate secrets periodically
  • Log signature failures for security monitoring

Testing

The signature middleware includes comprehensive test coverage for all features and edge cases:
Test CaseDescriptionExpected Behavior
TestNewBasic middleware creation with default optionsGET requests skip validation (default behavior)
TestValidSignatureValid HMAC-SHA256 signature verificationRequest accepted with 200 OK
TestInvalidSignatureInvalid signature providedRequest rejected with 401 Unauthorized
TestMissingSignatureNo signature header presentRequest rejected with 401 Unauthorized
TestSignaturePrefixSignature with prefix (e.g., “sha256=“)Prefix validated and stripped correctly
TestBase64EncodingBase64-encoded signature verificationSignature decoded and verified
TestSHA1AlgorithmSHA-1 algorithm (legacy support)Signature verified with SHA-1 HMAC
TestSHA512AlgorithmSHA-512 algorithm verificationSignature verified with SHA-512 HMAC
TestSkipPathsConfigured paths excluded from validationSkipped paths accept requests without signatures
TestCustomHeaderNameCustom signature header nameSignature read from custom header
TestCustomErrorHandlerCustom error response handlerCustom error format returned on failure
TestOnValidCallback function on successful validationOnValid callback executed
TestGetInfoRetrieve signature info from contextInfo object contains validation details
TestIsValidCheck if signature is validReturns true for valid signatures
TestGitHubGitHub webhook signature formatX-Hub-Signature-256 with sha256= prefix verified
TestSignSignature generation for various algorithmsConsistent signatures generated for same input
TestSignerClient-side request signingSignature header added to outgoing requests
TestSignerWithPrefixSigner with prefix configurationSignature includes configured prefix
TestSignerNilBodySigning request with nil bodyHandles nil body gracefully
TestStripeStripe webhook signature verificationStripe-Signature header verified
TestSlackSlack signature with timestamp formatv0=HMAC(v0:timestamp:body) format verified
TestTwilioTwilio signature with form parametersSHA-1 Base64 signature of URL + sorted params
TestAWSAWS Signature Version 4 (simplified)Authorization header signature verified
TestGetInfo_NoContextGetInfo without signature middlewareReturns nil when middleware not used
TestIsValid_NoContextIsValid without signature middlewareReturns false when middleware not used
TestSignaturePrefix_MissingRequired prefix not presentRequest rejected with 401 Unauthorized
TestCustomPayloadGetterCustom payload extraction functionUses custom payload for verification
TestCustomPayloadGetter_ErrorPayloadGetter returns errorRequest rejected with 401 Unauthorized
TestSignUnknownAlgorithmUnknown algorithm specifiedDefaults to SHA-256
TestSkipMethods_CustomCustom skip methods configurationConfigured methods skip validation
TestSignerSign_DirectDirect Sign method on SignerSignature added with all custom options
  • keyauth - API key authentication
  • jwt - JWT authentication
  • hmac - HMAC utilities