forked from jricher/oauth-spec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
draft-richer-oauth-signed-http-request.xml
279 lines (228 loc) · 12.3 KB
/
draft-richer-oauth-signed-http-request.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
<?xml version="1.0" encoding="US-ASCII"?>
<!DOCTYPE rfc SYSTEM "rfc2629.dtd">
<?rfc toc="yes"?>
<?rfc tocompact="yes"?>
<?rfc tocdepth="3"?>
<?rfc tocindent="yes"?>
<?rfc symrefs="yes"?>
<?rfc sortrefs="yes"?>
<?rfc comments="yes"?>
<?rfc inline="yes"?>
<?rfc compact="yes"?>
<?rfc subcompact="no"?>
<rfc category="exp" docName="draft-richer-oauth-signed-http-request"
ipr="trust200902">
<front>
<title abbrev="Abbreviated-Title">A Method for Signing an HTTP Requests
for OAuth</title>
<author fullname="Justin Richer" initials="J." role="editor"
surname="Richer">
<organization>The MITRE Corporation</organization>
</author>
<author fullname="John Bradley" initials="J." surname="Bradley">
<organization/>
<address>
<postal>
<street/>
<city/>
<region/>
<code/>
<country/>
</postal>
<phone/>
<facsimile/>
<email/>
<uri/>
</address>
</author>
<date day="3" month="March" year="2014"/>
<area>Security</area>
<workgroup>OAuth</workgroup>
<abstract>
<t>This draft proposes a simple method for creating a JSON object
structure based on an HTTP Request Message suitable for use in the bode
of a JWT (signed with JWS and/or encrypted with JWE). This draft also
describes a method of validating the portions of the HTTP message that
are included in the signed JWT. </t>
</abstract>
<note title="Requirements Language">
<t>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in <xref
target="RFC2119">RFC 2119</xref>.</t>
</note>
</front>
<middle>
<section title="Introduction">
<t>In order to protect an HTTP request with a signature, we need a
method of attaching that request to a cryptographic envelope and
calculating a signature over it. Ideally, this should be done without
replicating the information already present in the HTTP request.
Additionally, most forms of canonicalization found in past attempts have
failed implementation tests. It is acknowledged by this draft that
common HTTP application frameworks can and will insert extra headers,
query parameters, and otherwise manipulate the HTTP request on its way
from the web server into the application code itself. It is the goal of
this draft to have a signature protection mechanism that is robust
against such implementation issues. In the OAuth world, this signable
object is meant to be created by the Client and verified by the Resource
Server.</t>
</section>
<section title="Generating a JSON Object from an HTTP Request">
<t>This section describes how to generate a JSON object appropriate for
inclusion into a JWT's claim set. Each of these sections below is
included as a member of the JSON object. All members are OPTIONAL.</t>
<t><list style="hanging">
<t hangText="m">The HTTP Method used to make this requst. This MUST
be the uppercase HTTP verb as a JSON string.</t>
<t hangText="h">The HTTP URL host component as a JSON string. This
MAY include the port separated from the host by a colon in host:port
format.</t>
<t hangText="p">The HTTP URL path component of the requst as an HTTP
string.</t>
<t hangText="q">The hashed HTTP URL query parameter map of the
request as a two-part JSON array. The first part of this array is a
JSON array listing all query parameters that were used in the
calculation of the hash in the order that they were added to the
hashed value as described below. The second part of this array is a
JSON string containing the Base64URL encoded hash itself, calculated
as described below.</t>
<t hangText="h">The hashed HTTP request headers as a two-part JSON
array. The first part of this array is a JSON array listing all
headers that were used in the calculation of the hash in the order
that they were added to the hashed value as described below. The
second part of this array is a JSON string containing the Base64URL
encoded hash itself, calculated as described below.</t>
<t hangText="b">The base64URL encoded hash of the HTTP Request body,
calculated as the HMAC of the byte array of the body.</t>
</list></t>
<section title="Selection of a hashing algorithm and size">
<t>The hashes SHALL be calculated using the HMAC algorithm using a
hash size equal to the size of the surrounding JWT's alg header field.
That is, if the JWT uses HS256 or RS256, the HMAC here uses a 256-bit
HMAC. If the JWT uses RS512, the HMAC here uses 512-bit HMAC, and so
forth.</t>
</section>
<section title="Calculating the query parameter list and hash">
<t>To generate the query parameter list and hash, the client creates
two data objects: an ordered list of strings to hold the query
parameter names and a string buffer to hold the data to be hashed.</t>
<t>The client iterates through all query parameters in whatever order
it chooses and for each query parameter it does the following:</t>
<t><list style="numbers">
<t>Adds the name of the query parameter to the end of the
list.</t>
<t>Encodes the name and value of the query parameter as
"name=value" and appends it to the string buffer. [[Separated by
an ampersand? Alternatively we could have this also pulled into an
ordered list and post-process the concatenation, but that might be
too deep into the weeds. ]]</t>
</list>Repeated parameter names are processed separately with no
special handling. Parameters MAY be skipped by the client if they are
not required (or desired) to be covered by the signature.</t>
<t>The client then calculates the HMAC hash over the resulting string
buffer. The list and the hash result are added as the value of the "p"
member.</t>
</section>
<section title="Calculating the header list and hash">
<t>To generate the header list and hash, the client creates two data
objects: an ordered list of strings to hold the header names and a
string buffer to hold the data to be hashed.</t>
<t>The client iterates through all query parameters in whatever order
it chooses and for each query parameter it does the following:</t>
<t><list style="numbers">
<t>Adds the name of the header to the end of the list.</t>
<t>Encodes the name and value of the header as "name: value" and
appends it to the string buffer. [[Separated by a newline?
Alternatively we could have this also pulled into an ordered list
and post-process the concatenation, but that might be too deep
into the weeds. ]]</t>
</list>Repeated header names are processed separately with no
special handling. Headers MAY be skipped by the client if they are not
required (or desired) to be covered by the signature.</t>
<t>The client then calculates the HMAC hash over the resulting string
buffer. The list and the hash result are added as the value of the "h"
member.</t>
</section>
</section>
<section title="Validating the Hashes">
<t>Validation of the overall signature is done using the standard JWS
mechanisms for a signed JWT. However, in order to trust any of the
hashed mechanisms above, an application MUST re-create and validate a
hash for each component. </t>
<section title="Validating the query parameter list and hash">
<t>The client has at its disposal a map that indexes the query
parameter names to the values given. The client creates a string
buffer for calculating the hash. The client then iterates through the
"list" portion of the "p" parmaeter. For each item in the list (in the
order of the list) it does the following:</t>
<t><list style="numbers">
<t>Fetch the value of the parameter from the HTTP request
parameter map. If a parameter is found in the list of signed
parameters but not in the map, the validation fails.</t>
<t>Encode the parameter as "name=value" and concatenate it to the
end of the string buffer. [[ same separator issue as above ]]</t>
</list>[[ Repeated parameters could be problematic here depending on
order preservation properties of the server's handling of them. Most
I've seen use arrays for that though. ]]</t>
<t>The client calculates the hash of the string buffer and base64url
encodes it. The client compares that string to the string passed in as
the hash. If the two match, the hash validates, and all named
parameters and their values are considered covered by the signature.
</t>
<t>There MAY be additional query parameters that are not listed in the
list and are therefore not covered by the signature. The client MUST
decide whether or not to accept a request with these uncovered
parameters.</t>
</section>
<section title="Validating the header list and hash">
<t>The client has at its disposal a map that indexes the header names
to the values given. The client creates a string buffer for
calculating the hash. The client then iterates through the "list"
portion of the "h" parmaeter. For each item in the list (in the order
of the list) it does the following:</t>
<t><list style="numbers">
<t>Fetch the value of the header from the HTTP request header map.
If a header is found in the list of signed parameters but not in
the map, the validation fails.</t>
<t>Encode the parameter as "name: value" and concatenate it to the
end of the string buffer. [[ same separator issue as above ]]</t>
</list>[[ Repeated headers could be problematic here depending on
order preservation properties of the server's handling of them. Most
I've seen use arrays for that though. ]]</t>
<t>The client calculates the hash of the string buffer and base64url
encodes it. The client compares that string to the string passed in as
the hash. If the two match, the hash validates, and all named headers
and their values are considered covered by the signature.</t>
<t>There MAY be additional headers that are not listed in the list and
are therefore not covered by the signature. The client MUST decide
whether or not to accept a request with these uncovered headers.</t>
</section>
</section>
<section anchor="IANA" title="IANA Considerations">
<t>This document makes no request of IANA.</t>
<t>Note to RFC Editor: this section may be removed on publication as an
RFC.</t>
</section>
<section anchor="Security" title="Security Considerations">
<t>Since all components are only optionally protected by this method,
and even some components may be protected only in part (e.g., some
headers but not others, it is up to application developers to verify
that any key parameters in a request (beit method, query parameters,
headers, etc.) are actually covered by the signature container of the
JWT. The application verifying this signature MUST NOT assume that any
particular parameter is appropriately covered by the signature. Any
applications that are sensitive of header or query parameter order MUST
verify the order of the parameters on their own.</t>
</section>
<section anchor="Acknowledgements" title="Acknowledgements">
<t>The authors acknowledge the OAuth Working Group and submit this draft
for feedback and input into the ongoing work of signed tokens.</t>
</section>
</middle>
<back>
<references title="Normative References">
<?rfc include="reference.RFC.2119"?>
</references>
</back>
</rfc>