Skip to content

Commit e819b48

Browse files
committed
CLDSRV-847: test x-amz-content-256 is not required in signed headers
1 parent fa0e784 commit e819b48

File tree

2 files changed

+134
-10
lines changed

2 files changed

+134
-10
lines changed
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
const assert = require('assert');
2+
const async = require('async');
3+
const crypto = require('crypto');
4+
const { makeS3Request } = require('../utils/makeRequest');
5+
const HttpRequestAuthV4 = require('../utils/HttpRequestAuthV4');
6+
const url = require('url');
7+
8+
const bucket = 'testunsignedcontentshabucket';
9+
const objectKey = 'key';
10+
const objData = Buffer.alloc(1024, 'a');
11+
12+
const config = require('../../config.json');
13+
const authCredentials = {
14+
accessKey: config.accessKey,
15+
secretKey: config.secretKey,
16+
};
17+
18+
class HttpRequestAuthV4NoSHA256SignedHeader extends HttpRequestAuthV4 {
19+
constructor(url, params, callback) {
20+
super(url, params, callback);
21+
}
22+
23+
_constructRequest() {
24+
const dateObj = new Date();
25+
const isoDate = dateObj.toISOString();
26+
this._timestamp = [
27+
isoDate.slice(0, 4),
28+
isoDate.slice(5, 7),
29+
isoDate.slice(8, 13),
30+
isoDate.slice(14, 16),
31+
isoDate.slice(17, 19),
32+
'Z',
33+
].join('');
34+
35+
const urlObj = new url.URL(this._url);
36+
const signedHeaders = {
37+
'host': urlObj.host,
38+
'x-amz-date': this._timestamp,
39+
};
40+
const httpHeaders = Object.assign({}, this._httpParams.headers);
41+
Object.keys(httpHeaders).forEach(header => {
42+
const lowerHeader = header.toLowerCase();
43+
if (!['connection', 'transfer-encoding', 'x-amz-content-sha256'].includes(lowerHeader)) {
44+
signedHeaders[lowerHeader] = httpHeaders[header];
45+
}
46+
});
47+
httpHeaders.Authorization =
48+
this.getAuthorizationHeader(urlObj, signedHeaders, httpHeaders['x-amz-content-sha256']);
49+
return Object.assign(httpHeaders, signedHeaders);
50+
}
51+
}
52+
53+
describe('unsigned x-amz-content-sha256 header in AuthV4 requests:', () => {
54+
before(done => {
55+
makeS3Request({
56+
method: 'PUT',
57+
authCredentials,
58+
bucket,
59+
}, err => {
60+
assert.ifError(err);
61+
done();
62+
});
63+
});
64+
65+
after(done => {
66+
async.series([
67+
next => makeS3Request({
68+
method: 'DELETE',
69+
authCredentials,
70+
bucket,
71+
objectKey,
72+
}, next),
73+
next => makeS3Request({
74+
method: 'DELETE',
75+
authCredentials,
76+
bucket,
77+
}, next),
78+
], err => {
79+
assert.ifError(err);
80+
done();
81+
});
82+
});
83+
84+
it('should accept x-amz-content-sha256 header not in SignedHeaders list', done => {
85+
// Calculate the SHA256 hash of the data
86+
const contentSha256 = crypto.createHash('sha256')
87+
.update(objData)
88+
.digest('hex');
89+
90+
const req = new HttpRequestAuthV4NoSHA256SignedHeader(
91+
`http://localhost:8000/${bucket}/${objectKey}`,
92+
Object.assign(
93+
{
94+
method: 'PUT',
95+
headers: {
96+
'content-length': objData.length,
97+
'x-amz-content-sha256': contentSha256,
98+
},
99+
},
100+
authCredentials
101+
),
102+
res => {
103+
let body = '';
104+
res.on('data', chunk => {
105+
body += chunk;
106+
});
107+
res.on('end', () => {
108+
assert.strictEqual(body, '', 'expected empty body');
109+
assert.strictEqual(res.statusCode, 200,
110+
'Request should succeed even when x-amz-content-sha256 is not signed');
111+
done();
112+
});
113+
}
114+
);
115+
116+
req.on('error', err => {
117+
assert.ifError(err);
118+
});
119+
120+
req.write(objData);
121+
122+
req.once('drain', () => {
123+
req.end();
124+
});
125+
});
126+
});

tests/functional/raw-node/utils/HttpRequestAuthV4.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class HttpRequestAuthV4 extends stream.Writable {
8989
.update(stringToSign).digest('hex');
9090
}
9191

92-
getCanonicalRequest(urlObj, signedHeaders) {
92+
getCanonicalRequest(urlObj, signedHeaders, contentSha256) {
9393
const method = this._httpParams.method || 'GET';
9494
const signedHeadersList = Object.keys(signedHeaders).sort();
9595
const qsParams = [];
@@ -115,7 +115,7 @@ class HttpRequestAuthV4 extends stream.Writable {
115115
canonicalQueryString,
116116
canonicalSignedHeaders,
117117
signedHeadersList.join(';'),
118-
signedHeaders['x-amz-content-sha256'],
118+
contentSha256,
119119
].join('\n');
120120

121121
// console.log(`CANONICAL REQUEST: "${canonicalRequest}"`);
@@ -131,17 +131,17 @@ class HttpRequestAuthV4 extends stream.Writable {
131131
return stringToSign;
132132
}
133133

134-
getAuthorizationSignature(urlObj, signedHeaders) {
134+
getAuthorizationSignature(urlObj, signedHeaders, contentSha256) {
135135
const canonicalRequest =
136-
this.getCanonicalRequest(urlObj, signedHeaders);
136+
this.getCanonicalRequest(urlObj, signedHeaders, contentSha256);
137137
this._lastSignature = this.createSignature(
138138
this.constructRequestStringToSign(canonicalRequest));
139139
return this._lastSignature;
140140
}
141141

142-
getAuthorizationHeader(urlObj, signedHeaders) {
142+
getAuthorizationHeader(urlObj, signedHeaders, contentSha256) {
143143
const authorizationSignature =
144-
this.getAuthorizationSignature(urlObj, signedHeaders);
144+
this.getAuthorizationSignature(urlObj, signedHeaders, contentSha256);
145145
const signedHeadersList = Object.keys(signedHeaders).sort();
146146

147147
return ['AWS4-HMAC-SHA256',
@@ -206,8 +206,7 @@ class HttpRequestAuthV4 extends stream.Writable {
206206
if (lowerHeader === 'content-length') {
207207
contentLengthHeader = header;
208208
}
209-
if (!['connection',
210-
'transfer-encoding'].includes(lowerHeader)) {
209+
if (!['connection', 'transfer-encoding'].includes(lowerHeader)) {
211210
signedHeaders[lowerHeader] = httpHeaders[header];
212211
}
213212
});
@@ -229,8 +228,7 @@ class HttpRequestAuthV4 extends stream.Writable {
229228
}
230229
}
231230
httpHeaders.Authorization =
232-
this.getAuthorizationHeader(urlObj, signedHeaders);
233-
231+
this.getAuthorizationHeader(urlObj, signedHeaders, signedHeaders['x-amz-content-sha256']);
234232
return Object.assign(httpHeaders, signedHeaders);
235233
}
236234

0 commit comments

Comments
 (0)