httpsign

package
v0.0.0-...-4214099 Latest Latest
Warning

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

Go to latest
Published: Jun 19, 2017 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 CacheCapacity = 5000 * CacheTimeout // 5,000 msg/sec * 100 sec = 500,000 elements
View Source
const CacheTimeout = 100 // 100 sec
View Source
const MaxSkewSec = 5 // 5 sec
View Source
const XMailgunNonce = "X-Mailgun-Nonce"
View Source
const XMailgunSignature = "X-Mailgun-Signature"
View Source
const XMailgunSignatureVersion = "X-Mailgun-Signature-Version"
View Source
const 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 NonceCache

type NonceCache struct {
	sync.Mutex
	// contains filtered or unexported fields
}

func NewNonceCache

func NewNonceCache(capacity int, cacheTTL int, timeProvider timetools.TimeProvider) (*NonceCache, error)

Return a new NonceCache. Allows you to control cache capacity, ttl, as well as the TimeProvider.

func (*NonceCache) InCache

func (n *NonceCache) InCache(nonce string) bool

InCache checks if a nonce is in the cache. If not, it adds it to the cache and returns false. Otherwise it returns true.

type Service

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

Represents a service that can be used to sign and authenticate requests.

func New

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

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

func NewWithProviders

func NewWithProviders(config *Config, timeProvider timetools.TimeProvider,
	randomProvider random.RandomProvider) (*Service, error)

Returns a new Service. Provides control over time and random providers.

func (*Service) AuthenticateRequest

func (s *Service) AuthenticateRequest(r *http.Request) error

Authenticates HTTP request to ensure it was sent by an authorized sender.

func (*Service) AuthenticateRequestWithKey

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

Authenticates HTTP request to ensure it was sent by an authorized sender. Checks message signature with the passed in key, not the one initialized with.

func (*Service) SignRequest

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

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

func (*Service) SignRequestWithKey

func (s *Service) 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.

Jump to

Keyboard shortcuts

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