14
14
* limitations under the License.
15
15
*/
16
16
17
- import utils from "./utils.js" ;
17
+ import awscred from "./awscredentials.js" ;
18
+ import utils from "./utils.js" ;
18
19
19
20
const mod_hmac = require ( 'crypto' ) ;
20
21
@@ -28,8 +29,8 @@ const EMPTY_PAYLOAD_HASH = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495
28
29
* Constant defining the headers being signed.
29
30
* @type {string }
30
31
*/
31
- const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date' ;
32
-
32
+ // const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date';
33
+ const DEFAULT_SIGNED_HEADERS = 'host;x-amz-date' ;
33
34
34
35
/**
35
36
* Create HTTP Authorization header for authenticating with an AWS compatible
@@ -48,13 +49,13 @@ const DEFAULT_SIGNED_HEADERS = 'host;x-amz-content-sha256;x-amz-date';
48
49
function signatureV4 ( r , timestamp , region , service , uri , queryParams , host , credentials ) {
49
50
const eightDigitDate = utils . getEightDigitDate ( timestamp ) ;
50
51
const amzDatetime = utils . getAmzDatetime ( timestamp , eightDigitDate ) ;
51
- const canonicalRequest = _buildCanonicalRequest (
52
+ const canonicalRequest = _buildCanonicalRequest ( r ,
52
53
r . method , uri , queryParams , host , amzDatetime , credentials . sessionToken ) ;
53
54
const signature = _buildSignatureV4 ( r , amzDatetime , eightDigitDate ,
54
55
credentials , region , service , canonicalRequest ) ;
55
56
const authHeader = 'AWS4-HMAC-SHA256 Credential='
56
57
. concat ( credentials . accessKeyId , '/' , eightDigitDate , '/' , region , '/' , service , '/aws4_request,' ,
57
- 'SignedHeaders=' , _signedHeaders ( credentials . sessionToken ) , ',Signature=' , signature ) ;
58
+ 'SignedHeaders=' , _signedHeaders ( r , credentials . sessionToken ) , ',Signature=' , signature ) ;
58
59
59
60
utils . debug_log ( r , 'AWS v4 Auth header: [' + authHeader + ']' ) ;
60
61
@@ -73,22 +74,22 @@ function signatureV4(r, timestamp, region, service, uri, queryParams, host, cred
73
74
* @returns {string } string with concatenated request parameters
74
75
* @private
75
76
*/
76
- function _buildCanonicalRequest ( method , uri , queryParams , host , amzDatetime , sessionToken ) {
77
+ function _buildCanonicalRequest ( r ,
78
+ method , uri , queryParams , host , amzDatetime , sessionToken ) {
79
+ const payloadHash = awsHeaderPayloadHash ( r ) ;
77
80
let canonicalHeaders = 'host:' + host + '\n' +
78
- 'x-amz-content-sha256:' + EMPTY_PAYLOAD_HASH + '\n' +
79
- 'x-amz-date:' + amzDatetime + '\n' ;
81
+ 'x-amz-date:' + amzDatetime + '\n' ;
80
82
81
- if ( sessionToken ) {
83
+ if ( sessionToken && sessionToken . length > 0 ) {
82
84
canonicalHeaders += 'x-amz-security-token:' + sessionToken + '\n'
83
85
}
84
86
85
87
let canonicalRequest = method + '\n' ;
86
88
canonicalRequest += uri + '\n' ;
87
89
canonicalRequest += queryParams + '\n' ;
88
90
canonicalRequest += canonicalHeaders + '\n' ;
89
- canonicalRequest += _signedHeaders ( sessionToken ) + '\n' ;
90
- canonicalRequest += EMPTY_PAYLOAD_HASH ;
91
-
91
+ canonicalRequest += _signedHeaders ( r , sessionToken ) + '\n' ;
92
+ canonicalRequest += payloadHash ;
92
93
return canonicalRequest ;
93
94
}
94
95
@@ -119,7 +120,7 @@ function _buildSignatureV4(
119
120
const stringToSign = _buildStringToSign (
120
121
amzDatetime , eightDigitDate , region , service , canonicalRequestHash ) ;
121
122
122
- utils . debug_log ( r , 'AWS v4 Auth Signing String: [' + stringToSign + ']' ) ;
123
+ utils . debug_log ( r , 'AWS v4 Auth Signing String: [' + stringToSign + ']' ) ;
123
124
124
125
let kSigningHash ;
125
126
@@ -145,13 +146,13 @@ function _buildSignatureV4(
145
146
* we encode it as JSON. By doing so we can gracefully decode it
146
147
* when reading from the cache. */
147
148
kSigningHash = Buffer . from ( JSON . parse ( fields [ 1 ] ) ) ;
148
- // Otherwise, generate a new signing key hash and store it in the cache
149
+ // Otherwise, generate a new signing key hash and store it in the cache
149
150
} else {
150
151
kSigningHash = _buildSigningKeyHash ( creds . secretAccessKey , eightDigitDate , region , service ) ;
151
152
utils . debug_log ( r , 'Writing key: ' + eightDigitDate + ':' + kSigningHash . toString ( 'hex' ) ) ;
152
153
r . variables . signing_key_hash = eightDigitDate + ':' + JSON . stringify ( kSigningHash ) ;
153
154
}
154
- // Otherwise, don't use caching at all (like when we are using NGINX OSS)
155
+ // Otherwise, don't use caching at all (like when we are using NGINX OSS)
155
156
} else {
156
157
kSigningHash = _buildSigningKeyHash ( creds . secretAccessKey , eightDigitDate , region , service ) ;
157
158
}
@@ -190,13 +191,15 @@ function _buildStringToSign(amzDatetime, eightDigitDate, region, service, canoni
190
191
* Creates a string containing the headers that need to be signed as part of v4
191
192
* signature authentication.
192
193
*
194
+ * @param r {Request} HTTP request object
193
195
* @param sessionToken {string|undefined} AWS session token if present
194
196
* @returns {string } semicolon delimited string of the headers needed for signing
195
197
* @private
196
198
*/
197
- function _signedHeaders ( sessionToken ) {
198
- let headers = DEFAULT_SIGNED_HEADERS ;
199
- if ( sessionToken ) {
199
+ function _signedHeaders ( r , sessionToken ) {
200
+ let headers = '' ;
201
+ headers += DEFAULT_SIGNED_HEADERS ;
202
+ if ( sessionToken && sessionToken . length > 0 ) {
200
203
headers += ';x-amz-security-token' ;
201
204
}
202
205
return headers ;
@@ -250,8 +253,40 @@ function _splitCachedValues(cached) {
250
253
return [ eightDigitDate , kSigningHash ]
251
254
}
252
255
256
+ /**
257
+ * Outputs the timestamp used to sign the request, so that it can be added to
258
+ * the 'x-amz-date' header and sent by NGINX. The output format is
259
+ * ISO 8601: YYYYMMDD'T'HHMMSS'Z'.
260
+ * @see {@link https://docs.aws.amazon.com/general/latest/gr/sigv4-date-handling.html | Handling dates in Signature Version 4 }
261
+ *
262
+ * @param r {Request} HTTP request object (not used, but required for NGINX configuration)
263
+ * @returns {string } ISO 8601 timestamp
264
+ */
265
+ function awsHeaderDate ( r ) {
266
+ return utils . getAmzDatetime (
267
+ awscred . getNow ( ) ,
268
+ utils . getEightDigitDate ( awscred . getNow ( ) )
269
+ ) ;
270
+ }
271
+
272
+ /**
273
+ * Return a payload hash in the header
274
+ *
275
+ * @param r {Request} HTTP request object
276
+ * @returns {string } payload hash
277
+ */
278
+ function awsHeaderPayloadHash ( r ) {
279
+ const reqBody = ( r . variables . request_body === 'undefined' ) ? '' :
280
+ r . variables . request_body ;
281
+ const payloadHash = mod_hmac . createHash ( 'sha256' , 'utf8' )
282
+ . update ( reqBody )
283
+ . digest ( 'hex' ) ;
284
+ return payloadHash ;
285
+ }
253
286
254
287
export default {
288
+ awsHeaderDate,
289
+ awsHeaderPayloadHash,
255
290
signatureV4,
256
291
// These functions do not need to be exposed, but they are exposed so that
257
292
// unit tests can run against them.
0 commit comments