twitst4tz

twitter statistics web application
Log | Files | Refs | README | LICENSE

http_signing.md (13849B)


      1 # Abstract
      2 
      3 This document describes a way to add origin authentication, message integrity,
      4 and replay resistance to HTTP REST requests.  It is intended to be used over
      5 the HTTPS protocol.
      6 
      7 # Copyright Notice
      8 
      9 Copyright (c) 2011 Joyent, Inc. and the persons identified as document authors.
     10 All rights reserved.
     11 
     12 Code Components extracted from this document must include MIT License text.
     13 
     14 # Introduction
     15 
     16 This protocol is intended to provide a standard way for clients to sign HTTP
     17 requests.  RFC2617 (HTTP Authentication) defines Basic and Digest authentication
     18 mechanisms, and RFC5246 (TLS 1.2) defines client-auth, both of which are widely
     19 employed on the Internet today.  However, it is common place that the burdens of
     20 PKI prevent web service operators from deploying that methodology, and so many
     21 fall back to Basic authentication, which has poor security characteristics.
     22 
     23 Additionally, OAuth provides a fully-specified alternative for authorization
     24 of web service requests, but is not (always) ideal for machine to machine
     25 communication, as the key acquisition steps (generally) imply a fixed
     26 infrastructure that may not make sense to a service provider (e.g., symmetric
     27 keys).
     28 
     29 Several web service providers have invented their own schemes for signing
     30 HTTP requests, but to date, none have been placed in the public domain as a
     31 standard.  This document serves that purpose.  There are no techniques in this
     32 proposal that are novel beyond previous art, however, this aims to be a simple
     33 mechanism for signing these requests.
     34 
     35 # Signature Authentication Scheme
     36 
     37 The "signature" authentication scheme is based on the model that the client must
     38 authenticate itself with a digital signature produced by either a private
     39 asymmetric key (e.g., RSA) or a shared symmetric key (e.g., HMAC).  The scheme
     40 is parameterized enough such that it is not bound to any particular key type or
     41 signing algorithm.  However, it does explicitly assume that clients can send an
     42 HTTP `Date` header.
     43 
     44 ## Authorization Header
     45 
     46 The client is expected to send an Authorization header (as defined in RFC 2617)
     47 with the following parameterization:
     48 
     49     credentials := "Signature" params
     50     params := 1#(keyId | algorithm | [headers] | [ext] | signature)
     51     digitalSignature := plain-string
     52 
     53     keyId := "keyId" "=" <"> plain-string <">
     54     algorithm := "algorithm" "=" <"> plain-string <">
     55     headers := "headers" "=" <"> 1#headers-value <">
     56     ext := "ext" "=" <"> plain-string <">
     57     signature := "signature" "=" <"> plain-string <">
     58 
     59     headers-value := plain-string
     60     plain-string   = 1*( %x20-21 / %x23-5B / %x5D-7E )
     61 
     62 ### Signature Parameters
     63 
     64 #### keyId
     65 
     66 REQUIRED.  The `keyId` field is an opaque string that the server can use to look
     67 up the component they need to validate the signature.  It could be an SSH key
     68 fingerprint, an LDAP DN, etc.  Management of keys and assignment of `keyId` is
     69 out of scope for this document.
     70 
     71 #### algorithm
     72 
     73 REQUIRED. The `algorithm` parameter is used if the client and server agree on a
     74 non-standard digital signature algorithm.  The full list of supported signature
     75 mechanisms is listed below.
     76 
     77 #### headers
     78 
     79 OPTIONAL.  The `headers` parameter is used to specify the list of HTTP headers
     80 used to sign the request.  If specified, it should be a quoted list of HTTP
     81 header names, separated by a single space character.  By default, only one
     82 HTTP header is signed, which is the `Date` header.  Note that the list MUST be
     83 specified in the order the values are concatenated together during signing. To
     84 include the HTTP request line in the signature calculation, use the special
     85 `request-line` value.  While this is overloading the definition of `headers` in
     86 HTTP linguism, the request-line is defined in RFC 2616, and as the outlier from
     87 headers in useful signature calculation, it is deemed simpler to simply use
     88 `request-line` than to add a separate parameter for it.
     89 
     90 #### extensions
     91 
     92 OPTIONAL.  The `extensions` parameter is used to include additional information
     93 which is covered by the request.  The content and format of the string is out of
     94 scope for this document, and expected to be specified by implementors.
     95 
     96 #### signature
     97 
     98 REQUIRED.  The `signature` parameter is a `Base64` encoded digital signature
     99 generated by the client. The client uses the `algorithm` and `headers` request
    100 parameters to form a canonicalized `signing string`.  This `signing string` is
    101 then signed with the key associated with `keyId` and the algorithm
    102 corresponding to `algorithm`.  The `signature` parameter is then set to the
    103 `Base64` encoding of the signature.
    104 
    105 ### Signing String Composition
    106 
    107 In order to generate the string that is signed with a key, the client MUST take
    108 the values of each HTTP header specified by `headers` in the order they appear.
    109 
    110 1. If the header name is not `request-line` then append the lowercased header
    111    name followed with an ASCII colon `:` and an ASCII space ` `.
    112 2. If the header name is `request-line` then append the HTTP request line,
    113    otherwise append the header value.
    114 3. If value is not the last value then append an ASCII newline `\n`. The string
    115    MUST NOT include a trailing ASCII newline.
    116 
    117 # Example Requests
    118 
    119 All requests refer to the following request (body omitted):
    120 
    121     POST /foo HTTP/1.1
    122     Host: example.org
    123     Date: Tue, 07 Jun 2014 20:51:35 GMT
    124     Content-Type: application/json
    125     Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
    126     Content-Length: 18
    127 
    128 The "rsa-key-1" keyId refers to a private key known to the client and a public
    129 key known to the server. The "hmac-key-1" keyId refers to key known to the
    130 client and server.
    131 
    132 ## Default parameterization
    133 
    134 The authorization header and signature would be generated as:
    135 
    136     Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",signature="Base64(RSA-SHA256(signing string))"
    137 
    138 The client would compose the signing string as:
    139 
    140     date: Tue, 07 Jun 2014 20:51:35 GMT
    141 
    142 ## Header List
    143 
    144 The authorization header and signature would be generated as:
    145 
    146     Authorization: Signature keyId="rsa-key-1",algorithm="rsa-sha256",headers="(request-target) date content-type digest",signature="Base64(RSA-SHA256(signing string))"
    147 
    148 The client would compose the signing string as (`+ "\n"` inserted for
    149 readability):
    150 
    151     (request-target) post /foo + "\n"
    152     date: Tue, 07 Jun 2011 20:51:35 GMT + "\n"
    153     content-type: application/json + "\n"
    154     digest: SHA-256=Base64(SHA256(Body))
    155 
    156 ## Algorithm
    157 
    158 The authorization header and signature would be generated as:
    159 
    160     Authorization: Signature keyId="hmac-key-1",algorithm="hmac-sha1",signature="Base64(HMAC-SHA1(signing string))"
    161 
    162 The client would compose the signing string as:
    163 
    164     date: Tue, 07 Jun 2011 20:51:35 GMT
    165 
    166 # Signing Algorithms
    167 
    168 Currently supported algorithm names are:
    169 
    170 * rsa-sha1
    171 * rsa-sha256
    172 * rsa-sha512
    173 * dsa-sha1
    174 * hmac-sha1
    175 * hmac-sha256
    176 * hmac-sha512
    177 
    178 # Security Considerations
    179 
    180 ## Default Parameters
    181 
    182 Note the default parameterization of the `Signature` scheme is only safe if all
    183 requests are carried over a secure transport (i.e., TLS).  Sending the default
    184 scheme over a non-secure transport will leave the request vulnerable to
    185 spoofing, tampering, replay/repudiation, and integrity violations (if using the
    186 STRIDE threat-modeling methodology).
    187 
    188 ## Insecure Transports
    189 
    190 If sending the request over plain HTTP, service providers SHOULD require clients
    191 to sign ALL HTTP headers, and the `request-line`.  Additionally, service
    192 providers SHOULD require `Content-MD5` calculations to be performed to ensure
    193 against any tampering from clients.
    194 
    195 ## Nonces
    196 
    197 Nonces are out of scope for this document simply because many service providers
    198 fail to implement them correctly, or do not adopt security specifications
    199 because of the infrastructure complexity.  Given the `header` parameterization,
    200 a service provider is fully enabled to add nonce semantics into this scheme by
    201 using something like an `x-request-nonce` header, and ensuring it is signed
    202 with the `Date` header.
    203 
    204 ## Clock Skew
    205 
    206 As the default scheme is to sign the `Date` header, service providers SHOULD
    207 protect against logged replay attacks by enforcing a clock skew.  The server
    208 SHOULD be synchronized with NTP, and the recommendation in this specification
    209 is to allow 300s of clock skew (in either direction).
    210 
    211 ## Required Headers to Sign
    212 
    213 It is out of scope for this document to dictate what headers a service provider
    214 will want to enforce, but service providers SHOULD at minimum include the
    215 `Date` header.
    216 
    217 # References
    218 
    219 ## Normative References
    220 
    221 * [RFC2616] Hypertext Transfer Protocol -- HTTP/1.1
    222 * [RFC2617] HTTP Authentication: Basic and Digest Access Authentication
    223 * [RFC5246] The Transport Layer Security (TLS) Protocol Version 1.2
    224 
    225 ## Informative References
    226 
    227     Name: Mark Cavage (editor)
    228     Company: Joyent, Inc.
    229     Email: mark.cavage@joyent.com
    230     URI: http://www.joyent.com
    231 
    232 # Appendix A - Test Values
    233 
    234 The following test data uses the RSA (1024b) keys, which we will refer
    235 to as `keyId=Test` in the following samples:
    236 
    237     -----BEGIN PUBLIC KEY-----
    238     MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
    239     6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6
    240     Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw
    241     oYi+1hqp1fIekaxsyQIDAQAB
    242     -----END PUBLIC KEY-----
    243 
    244     -----BEGIN RSA PRIVATE KEY-----
    245     MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF
    246     NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F
    247     UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB
    248     AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA
    249     QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK
    250     kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
    251     f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u
    252     412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc
    253     mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7
    254     kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
    255     gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
    256     G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
    257     7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA==
    258     -----END RSA PRIVATE KEY-----
    259 
    260 And all examples use this request:
    261 
    262 <!-- httpreq -->
    263 
    264     POST /foo?param=value&pet=dog HTTP/1.1
    265     Host: example.com
    266     Date: Thu, 05 Jan 2014 21:31:40 GMT
    267     Content-Type: application/json
    268     Digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
    269     Content-Length: 18
    270 
    271     {"hello": "world"}
    272 
    273 <!-- /httpreq -->
    274 
    275 ### Default
    276 
    277 The string to sign would be:
    278 
    279 <!-- sign {"name": "Default", "options": {"keyId":"Test", "algorithm": "rsa-sha256"}} -->
    280 <!-- signstring -->
    281 
    282     date: Thu, 05 Jan 2014 21:31:40 GMT
    283 
    284 <!-- /signstring -->
    285 
    286 The Authorization header would be:
    287 
    288 <!-- authz -->
    289 
    290     Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="date",signature="jKyvPcxB4JbmYY4mByyBY7cZfNl4OW9HpFQlG7N4YcJPteKTu4MWCLyk+gIr0wDgqtLWf9NLpMAMimdfsH7FSWGfbMFSrsVTHNTk0rK3usrfFnti1dxsM4jl0kYJCKTGI/UWkqiaxwNiKqGcdlEDrTcUhhsFsOIo8VhddmZTZ8w="
    291 
    292 <!-- /authz -->
    293 
    294 ### All Headers
    295 
    296 Parameterized to include all headers, the string to sign would be (`+ "\n"`
    297 inserted for readability):
    298 
    299 <!-- sign {"name": "All Headers", "options": {"keyId":"Test", "algorithm": "rsa-sha256", "headers": ["(request-target)", "host", "date", "content-type", "digest", "content-length"]}} -->
    300 <!-- signstring -->
    301 
    302     (request-target): post /foo?param=value&pet=dog
    303     host: example.com
    304     date: Thu, 05 Jan 2014 21:31:40 GMT
    305     content-type: application/json
    306     digest: SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=
    307     content-length: 18
    308 
    309 <!-- /signstring -->
    310 
    311 The Authorization header would be:
    312 
    313 <!-- authz -->
    314 
    315     Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="Ef7MlxLXoBovhil3AlyjtBwAL9g4TN3tibLj7uuNB3CROat/9KaeQ4hW2NiJ+pZ6HQEOx9vYZAyi+7cmIkmJszJCut5kQLAwuX+Ms/mUFvpKlSo9StS2bMXDBNjOh4Auj774GFj4gwjS+3NhFeoqyr/MuN6HsEnkvn6zdgfE2i0="
    316 
    317 <!-- /authz -->
    318 
    319 ## Generating and verifying signatures using `openssl`
    320 
    321 The `openssl` commandline tool can be used to generate or verify the signatures listed above.
    322 
    323 Compose the signing string as usual, and pipe it into the the `openssl dgst` command, then into `openssl enc -base64`, as follows:
    324 
    325     $ printf 'date: Thu, 05 Jan 2014 21:31:40 GMT' | \
    326       openssl dgst -binary -sign /path/to/private.pem -sha256 | \
    327       openssl enc -base64
    328     jKyvPcxB4JbmYY4mByyBY7cZfNl4OW9Hp...
    329     $
    330 
    331 The `-sha256` option is necessary to produce an `rsa-sha256` signature. You can select other hash algorithms such as `sha1` by changing this argument.
    332 
    333 To verify a signature, first save the signature data, Base64-decoded, into a file, then use `openssl dgst` again with the `-verify` option:
    334 
    335     $ echo 'jKyvPcxB4JbmYY4mByy...' | openssl enc -A -d -base64 > signature
    336     $ printf 'date: Thu, 05 Jan 2014 21:31:40 GMT' | \
    337       openssl dgst -sha256 -verify /path/to/public.pem -signature ./signature
    338     Verified OK
    339     $
    340 
    341 ## Generating and verifying signatures using `sshpk-sign`
    342 
    343 You can also generate and check signatures using the `sshpk-sign` tool which is
    344 included with the `sshpk` package in `npm`.
    345 
    346 Compose the signing string as above, and pipe it into `sshpk-sign` as follows:
    347 
    348     $ printf 'date: Thu, 05 Jan 2014 21:31:40 GMT' | \
    349       sshpk-sign -i /path/to/private.pem
    350     jKyvPcxB4JbmYY4mByyBY7cZfNl4OW9Hp...
    351     $
    352 
    353 This will produce an `rsa-sha256` signature by default, as you can see using
    354 the `-v` option:
    355 
    356     sshpk-sign: using rsa-sha256 with a 1024 bit key
    357 
    358 You can also use `sshpk-verify` in a similar manner:
    359 
    360     $ printf 'date: Thu, 05 Jan 2014 21:31:40 GMT' | \
    361       sshpk-verify -i ./public.pem -s 'jKyvPcxB4JbmYY...'
    362     OK
    363     $