http_signing
Abstract
This document describes a way to add origin authentication, message integrity, and replay resistance to HTTP REST requests. It is intended to be used over the HTTPS protocol.
Copyright Notice
Copyright (c) 2011 Joyent, Inc. and the persons identified as document authors. All rights reserved.
Code Components extracted from this document must include MIT License text.
Introduction
This protocol is intended to provide a standard way for clients to sign HTTP requests. RFC2617 (HTTP Authentication) defines Basic and Digest authentication mechanisms, and RFC5246 (TLS 1.2) defines client-auth, both of which are widely employed on the Internet today. However, it is common place that the burdens of PKI prevent web service operators from deploying that methodology, and so many fall back to Basic authentication, which has poor security characteristics.
Additionally, OAuth provides a fully-specified alternative for authorization of web service requests, but is not (always) ideal for machine to machine communication, as the key acquisition steps (generally) imply a fixed infrastructure that may not make sense to a service provider (e.g., symmetric keys).
Several web service providers have invented their own schemes for signing HTTP requests, but to date, none have been placed in the public domain as a standard. This document serves that purpose. There are no techniques in this proposal that are novel beyond previous art, however, this aims to be a simple mechanism for signing these requests.
Signature Authentication Scheme
The "signature" authentication scheme is based on the model that the client must authenticate itself with a digital signature produced by either a private asymmetric key (e.g., RSA) or a shared symmetric key (e.g., HMAC). The scheme is parameterized enough such that it is not bound to any particular key type or signing algorithm. However, it does explicitly assume that clients can send an HTTP Date
header.
Authorization Header
The client is expected to send an Authorization header (as defined in RFC 2617) with the following parameterization:
Signature Parameters
keyId
REQUIRED. The keyId
field is an opaque string that the server can use to look up the component they need to validate the signature. It could be an SSH key fingerprint, an LDAP DN, etc. Management of keys and assignment of keyId
is out of scope for this document.
algorithm
REQUIRED. The algorithm
parameter is used if the client and server agree on a non-standard digital signature algorithm. The full list of supported signature mechanisms is listed below.
headers
OPTIONAL. The headers
parameter is used to specify the list of HTTP headers used to sign the request. If specified, it should be a quoted list of HTTP header names, separated by a single space character. By default, only one HTTP header is signed, which is the Date
header. Note that the list MUST be specified in the order the values are concatenated together during signing. To include the HTTP request line in the signature calculation, use the special request-line
value. While this is overloading the definition of headers
in HTTP linguism, the request-line is defined in RFC 2616, and as the outlier from headers in useful signature calculation, it is deemed simpler to simply use request-line
than to add a separate parameter for it.
extensions
OPTIONAL. The extensions
parameter is used to include additional information which is covered by the request. The content and format of the string is out of scope for this document, and expected to be specified by implementors.
signature
REQUIRED. The signature
parameter is a Base64
encoded digital signature generated by the client. The client uses the algorithm
and headers
request parameters to form a canonicalized signing string
. This signing string
is then signed with the key associated with keyId
and the algorithm corresponding to algorithm
. The signature
parameter is then set to the Base64
encoding of the signature.
Signing String Composition
In order to generate the string that is signed with a key, the client MUST take the values of each HTTP header specified by headers
in the order they appear.
If the header name is not
request-line
then append the lowercased header name followed with an ASCII colon:
and an ASCII space .If the header name is
request-line
then append the HTTP request line, otherwise append the header value.If value is not the last value then append an ASCII newline . The string MUST NOT include a trailing ASCII newline.
Example Requests
All requests refer to the following request (body omitted):
The "rsa-key-1" keyId refers to a private key known to the client and a public key known to the server. The "hmac-key-1" keyId refers to key known to the client and server.
Default parameterization
The authorization header and signature would be generated as:
The client would compose the signing string as:
Header List
The authorization header and signature would be generated as:
The client would compose the signing string as (+ "\n"
inserted for readability):
Algorithm
The authorization header and signature would be generated as:
The client would compose the signing string as:
Signing Algorithms
Currently supported algorithm names are:
rsa-sha1
rsa-sha256
rsa-sha512
dsa-sha1
hmac-sha1
hmac-sha256
hmac-sha512
Security Considerations
Default Parameters
Note the default parameterization of the Signature
scheme is only safe if all requests are carried over a secure transport (i.e., TLS). Sending the default scheme over a non-secure transport will leave the request vulnerable to spoofing, tampering, replay/repudiation, and integrity violations (if using the STRIDE threat-modeling methodology).
Insecure Transports
If sending the request over plain HTTP, service providers SHOULD require clients to sign ALL HTTP headers, and the request-line
. Additionally, service providers SHOULD require Content-MD5
calculations to be performed to ensure against any tampering from clients.
Nonces
Nonces are out of scope for this document simply because many service providers fail to implement them correctly, or do not adopt security specifications because of the infrastructure complexity. Given the header
parameterization, a service provider is fully enabled to add nonce semantics into this scheme by using something like an x-request-nonce
header, and ensuring it is signed with the Date
header.
Clock Skew
As the default scheme is to sign the Date
header, service providers SHOULD protect against logged replay attacks by enforcing a clock skew. The server SHOULD be synchronized with NTP, and the recommendation in this specification is to allow 300s of clock skew (in either direction).
Required Headers to Sign
It is out of scope for this document to dictate what headers a service provider will want to enforce, but service providers SHOULD at minimum include the Date
header.
References
Normative References
[RFC2616] Hypertext Transfer Protocol -- HTTP/1.1
[RFC2617] HTTP Authentication: Basic and Digest Access Authentication
[RFC5246] The Transport Layer Security (TLS) Protocol Version 1.2
Informative References
Appendix A - Test Values
The following test data uses the RSA (1024b) keys, which we will refer to as keyId=Test
in the following samples:
And all examples use this request:
Default
The string to sign would be:
The Authorization header would be:
All Headers
Parameterized to include all headers, the string to sign would be (+ "\n"
inserted for readability):
The Authorization header would be:
Generating and verifying signatures using openssl
openssl
The openssl
commandline tool can be used to generate or verify the signatures listed above.
Compose the signing string as usual, and pipe it into the the openssl dgst
command, then into openssl enc -base64
, as follows:
The -sha256
option is necessary to produce an rsa-sha256
signature. You can select other hash algorithms such as sha1
by changing this argument.
To verify a signature, first save the signature data, Base64-decoded, into a file, then use openssl dgst
again with the -verify
option:
Generating and verifying signatures using sshpk-sign
sshpk-sign
You can also generate and check signatures using the sshpk-sign
tool which is included with the sshpk
package in npm
.
Compose the signing string as above, and pipe it into sshpk-sign
as follows:
This will produce an rsa-sha256
signature by default, as you can see using the -v
option:
You can also use sshpk-verify
in a similar manner:
Last updated