You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

363 lines
14 KiB

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