Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenko/cloudserver",
"version": "9.2.25",
"version": "9.2.26",
"description": "Zenko CloudServer, an open-source Node.js implementation of a server handling the Amazon S3 protocol",
"main": "index.js",
"engines": {
Expand All @@ -21,7 +21,7 @@
"dependencies": {
"@azure/storage-blob": "^12.28.0",
"@hapi/joi": "^17.1.1",
"arsenal": "git+https://github.com/scality/arsenal#8.2.44",
"arsenal": "git+https://github.com/scality/arsenal#8.2.45",
"async": "2.6.4",
"aws-sdk": "^2.1692.0",
"bucketclient": "scality/bucketclient#8.2.7",
Expand Down
126 changes: 126 additions & 0 deletions tests/functional/raw-node/test/unsignedChecksumHeaders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
const assert = require('assert');
const async = require('async');
const crypto = require('crypto');
const { makeS3Request } = require('../utils/makeRequest');
const HttpRequestAuthV4 = require('../utils/HttpRequestAuthV4');
const url = require('url');

const bucket = 'testunsignedcontentshabucket';
const objectKey = 'key';
const objData = Buffer.alloc(1024, 'a');

const config = require('../../config.json');
const authCredentials = {
accessKey: config.accessKey,
secretKey: config.secretKey,
};

class HttpRequestAuthV4NoSHA256SignedHeader extends HttpRequestAuthV4 {
constructor(url, params, callback) {
super(url, params, callback);
}

_constructRequest() {
const dateObj = new Date();
const isoDate = dateObj.toISOString();
this._timestamp = [
isoDate.slice(0, 4),
isoDate.slice(5, 7),
isoDate.slice(8, 13),
isoDate.slice(14, 16),
isoDate.slice(17, 19),
'Z',
].join('');

const urlObj = new url.URL(this._url);
const signedHeaders = {
'host': urlObj.host,
'x-amz-date': this._timestamp,
};
const httpHeaders = Object.assign({}, this._httpParams.headers);
Object.keys(httpHeaders).forEach(header => {
const lowerHeader = header.toLowerCase();
if (!['connection', 'transfer-encoding', 'x-amz-content-sha256'].includes(lowerHeader)) {
signedHeaders[lowerHeader] = httpHeaders[header];
}
});
httpHeaders.Authorization =
this.getAuthorizationHeader(urlObj, signedHeaders, httpHeaders['x-amz-content-sha256']);
return Object.assign(httpHeaders, signedHeaders);
}
}

describe('unsigned x-amz-content-sha256 header in AuthV4 requests:', () => {
before(done => {
makeS3Request({
method: 'PUT',
authCredentials,
bucket,
}, err => {
assert.ifError(err);
done();
});
});

after(done => {
async.series([
next => makeS3Request({
method: 'DELETE',
authCredentials,
bucket,
objectKey,
}, next),
next => makeS3Request({
method: 'DELETE',
authCredentials,
bucket,
}, next),
], err => {
assert.ifError(err);
done();
});
});

it('should accept x-amz-content-sha256 header not in SignedHeaders list', done => {
// Calculate the SHA256 hash of the data
const contentSha256 = crypto.createHash('sha256')
.update(objData)
.digest('hex');

const req = new HttpRequestAuthV4NoSHA256SignedHeader(
`http://localhost:8000/${bucket}/${objectKey}`,
Object.assign(
{
method: 'PUT',
headers: {
'content-length': objData.length,
'x-amz-content-sha256': contentSha256,
},
},
authCredentials
),
res => {
let body = '';
res.on('data', chunk => {
body += chunk;
});
res.on('end', () => {
assert.strictEqual(body, '', 'expected empty body');
assert.strictEqual(res.statusCode, 200,
'Request should succeed even when x-amz-content-sha256 is not signed');
done();
});
}
);

req.on('error', err => {
assert.ifError(err);
});

req.write(objData);

req.once('drain', () => {
req.end();
});
});
});
18 changes: 8 additions & 10 deletions tests/functional/raw-node/utils/HttpRequestAuthV4.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class HttpRequestAuthV4 extends stream.Writable {
.update(stringToSign).digest('hex');
}

getCanonicalRequest(urlObj, signedHeaders) {
getCanonicalRequest(urlObj, signedHeaders, contentSha256) {
const method = this._httpParams.method || 'GET';
const signedHeadersList = Object.keys(signedHeaders).sort();
const qsParams = [];
Expand All @@ -115,7 +115,7 @@ class HttpRequestAuthV4 extends stream.Writable {
canonicalQueryString,
canonicalSignedHeaders,
signedHeadersList.join(';'),
signedHeaders['x-amz-content-sha256'],
contentSha256,
].join('\n');

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

getAuthorizationSignature(urlObj, signedHeaders) {
getAuthorizationSignature(urlObj, signedHeaders, contentSha256) {
const canonicalRequest =
this.getCanonicalRequest(urlObj, signedHeaders);
this.getCanonicalRequest(urlObj, signedHeaders, contentSha256);
this._lastSignature = this.createSignature(
this.constructRequestStringToSign(canonicalRequest));
return this._lastSignature;
}

getAuthorizationHeader(urlObj, signedHeaders) {
getAuthorizationHeader(urlObj, signedHeaders, contentSha256) {
const authorizationSignature =
this.getAuthorizationSignature(urlObj, signedHeaders);
this.getAuthorizationSignature(urlObj, signedHeaders, contentSha256);
const signedHeadersList = Object.keys(signedHeaders).sort();

return ['AWS4-HMAC-SHA256',
Expand Down Expand Up @@ -206,8 +206,7 @@ class HttpRequestAuthV4 extends stream.Writable {
if (lowerHeader === 'content-length') {
contentLengthHeader = header;
}
if (!['connection',
'transfer-encoding'].includes(lowerHeader)) {
if (!['connection', 'transfer-encoding'].includes(lowerHeader)) {
signedHeaders[lowerHeader] = httpHeaders[header];
}
});
Expand All @@ -229,8 +228,7 @@ class HttpRequestAuthV4 extends stream.Writable {
}
}
httpHeaders.Authorization =
this.getAuthorizationHeader(urlObj, signedHeaders);

this.getAuthorizationHeader(urlObj, signedHeaders, signedHeaders['x-amz-content-sha256']);
return Object.assign(httpHeaders, signedHeaders);
}

Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1527,9 +1527,9 @@ arraybuffer.prototype.slice@^1.0.4:
optionalDependencies:
ioctl "^2.0.2"

"arsenal@git+https://github.com/scality/arsenal#8.2.44":
version "8.2.44"
resolved "git+https://github.com/scality/arsenal#960e77028b6eb614dde297e50a1530dcd9015f16"
"arsenal@git+https://github.com/scality/arsenal#8.2.45":
version "8.2.45"
resolved "git+https://github.com/scality/arsenal#af610f2510084a6e12a7c4c38b85eeb24a0e468c"
dependencies:
"@azure/identity" "^4.13.0"
"@azure/storage-blob" "^12.28.0"
Expand Down
Loading