httpsign

package
v4.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2022 License: Apache-2.0 Imports: 16 Imported by: 0

README

httpsign

Mailgun tools for signing and authenticating HTTP requests between web services.

Overview

An keyed-hash message authentication code (HMAC) is used to provide integrity and authenticity of a message between web services. The following elements are input into the HMAC. Only the items in bold are required to be passed in by the user, the other elements are either optional or build by httpsign for you.

  • Shared secret, a randomly generated number from a CSPRNG.
  • Timestamp in epoch time (number of seconds since January 1, 1970 UTC).
  • Nonce, a randomly generated number from a CSPRNG.
  • Request body.
  • Optionally the HTTP Verb and HTTP Request URI.
  • Optionally an additional headers to sign.

Each request element is delimited with the character | and each request element is preceded by its length. A simple example with only the required parameters:

shared_secret = '042DAD12E0BE4625AC0B2C3F7172DBA8'
timestamp     = '1330837567'
nonce         = '000102030405060708090a0b0c0d0e0f'
request_body  = '{"hello": "world"}'

signature     = HMAC('042DAD12E0BE4625AC0B2C3F7172DBA8',
   '10|1330837567|32|000102030405060708090a0b0c0d0e0f|18|{"hello": "world"}')

The timestamp, nonce, signature, and signature version are set as headers for the HTTP request to be signed. They are then verified on the receiving side by running the same algorithm and verifying that the signatures match.

Note: By default the service can securely handle authenticating 5,000 requests per second. If you need to authenticate more, increase the capacity of the nonce cache when initializing the package.

Examples

Signing a Request

import (
    "net/http"
    "strings"

    "github.com/mailgun/lemma/httpsign"
)

auths := httpsign.New(&httpsign.Config{Keypath: "/path/to/file.key"})

[...]

// build new request
requestBody := strings.NewReader(`{"hello":"world"}`)
request, _ := http.NewRequest("POST", "", requestBody)

// sign request
err := auths.SignRequest(request)
if err != nil {
    return err
}

// submit request
client := &http.Client{}
response, _ := client.Do(request)

Signing a Request with Headers

import (
    "net/http"
    "strings"

    "github.com/mailgun/lemma/httpsign"
)

auths := httpsign.New(&httpsign.Config{
    Keypath: "/path/to/file.key",
    HeadersToSign: []string{"X-Mailgun-Header"},
})

[...]

// build new request
requestBody := strings.NewReader(`{"hello":"world"}`)
request, _ := http.NewRequest("POST", "", requestBody)
request.Header.Set("X-Mailgun-Header", "foobar")

// sign request
err := auths.SignRequest(request)
if err != nil {
    return err
}

// submit request
client := &http.Client{}
response, _ := client.Do(request)

Signing a Request with HTTP Verb and URI

import (
    "net/http"
    "strings"

    "github.com/mailgun/lemma/httpsign"
)

auths := httpsign.New(&httpsign.Config{
    Keypath: "/path/to/file.key",
    SignVerbAndURI: true,
})

[...]

// build new request
requestBody := strings.NewReader(`{"hello":"world"}`)
request, _ := http.NewRequest("POST", "", requestBody)

// sign request
err := auths.SignRequest(request)
if err != nil {
    return err
}

// submit request
client := &http.Client{}
response, _ := client.Do(request)

Authenticating a Request

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/mailgun/lemma/httpsign"
)

auths := httpsign.New(&httpsign.Config{Keypath: "/path/to/file.key"})

[...]

func handler(w http.ResponseWriter, r *http.Request) {
    // authenticate request
    err := auths.AuthenticateRequest(r)
   
    // request is invalid
    if err != nil {
        fmt.Fprintf(w, "<p>Unable to Authenticate Request: %v</p>", err)
        return
    }
  
    // valid request
    fmt.Fprintf(w, "<p>Request Authenticated, welcome!</p>")
}

Documentation

Overview

Package httpsign provides tools for signing and authenticating HTTP requests between web services. See README.md for more details.

Index

Constants

View Source
const (
	XMailgunSignature        = "X-Mailgun-Signature"
	XMailgunSignatureVersion = "X-Mailgun-Signature-Version"
	XMailgunNonce            = "X-Mailgun-Nonce"
	XMailgunTimestamp        = "X-Mailgun-Timestamp"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// KeyPath is a path to a file that contains the key to sign requests. If
	// it is an empty string then the key should be provided in `KeyBytes`.
	KeyPath string

	// KeyBytes is a key that is used by lemma to sign requests. Ignored if
	// `KeyPath` is not an empty string.
	KeyBytes []byte

	HeadersToSign  []string // list of headers to sign
	SignVerbAndURI bool     // include the http verb and uri in request

	NonceCacheCapacity int // capacity of the nonce cache
	NonceCacheTimeout  int // nonce cache timeout

	EmitStats    bool   // toggle emitting metrics or not
	StatsdHost   string // hostname of statsd server
	StatsdPort   int    // port of statsd server
	StatsdPrefix string // prefix to prepend to metrics

	NonceHeaderName            string // default: X-Mailgun-Nonce
	TimestampHeaderName        string // default: X-Mailgun-Timestamp
	SignatureHeaderName        string // default: X-Mailgun-Signature
	SignatureVersionHeaderName string // default: X-Mailgun-Signature-Version
}

Modify NonceCacheCapacity and NonceCacheTimeout if your service needs to authenticate more than 5,000 requests per second. For example, if you need to handle 10,000 requests per second and timeout after one minute, you may want to set NonceCacheTimeout to 60 and NonceCacheCapacity to 10000 * cacheTimeout = 600000.

type SeededRNG

type SeededRNG struct {
	Seed int64
	// contains filtered or unexported fields
}

SeededRNG returns bytes generated in a predictable sequence by package math/rand. Not cryptographically secure, not thread safe. Changes to Seed after the first call to Bytes or HexDigest will have no effect. The zero value of SeededRNG is ready to use, and will use a seed of 0.

type Signer

type Signer struct {
	// contains filtered or unexported fields
}

Represents an entity that can be used to sign and authenticate requests.

func New

func New(config *Config) (*Signer, error)

Return a new Signer. Config can not be nil. If you need control over setting time and random providers, use NewWithProviders.

func (*Signer) SignRequest

func (s *Signer) SignRequest(r *http.Request) error

Signs a given HTTP request with signature, nonce, and timestamp.

func (*Signer) SignRequestWithKey

func (s *Signer) SignRequestWithKey(r *http.Request, secretKey []byte) error

Signs a given HTTP request with signature, nonce, and timestamp. Signs the message with the passed in key not the one initialized with.

func (*Signer) VerifyRequest

func (s *Signer) VerifyRequest(r *http.Request) error

VerifyRequest checks that an HTTP request was sent by an authorized sender.

func (*Signer) VerifyRequestWithKey

func (s *Signer) VerifyRequestWithKey(r *http.Request, secretKey []byte) (err error)

VerifyRequestWithKey checks that an HTTP request was sent by an authorized sender. The check is performed against the passed in key.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL