通过 Postman 使用 AWS4 签名进行 CRUD 弹性操作
Posted
技术标签:
【中文标题】通过 Postman 使用 AWS4 签名进行 CRUD 弹性操作【英文标题】:Using AWS4 Signature via Postman for CRUD Elastic operations 【发布时间】:2019-06-27 06:29:38 【问题描述】:我正在尝试使用 AWS4 签名方法将数据发布到由 AWS 管理的 Elasticsearch。我想通过邮递员预先脚本来实现这一点。我尝试使用以下脚本,该脚本非常适用于 Elastic 搜索的 GET 操作,但它不适用于 POST 或 PUT 或 DELETE 操作,并不断给我错误消息,指出签名与 POST 操作不匹配。有人可以帮我在邮递员中修复以下脚本吗?
var date = new Date().toISOString();
var amzdate = date.replace(/[:\-]|\.\d3/g, "");
var dateStamp = amzdate.slice(0, -8);
pm.environment.set('authorization', getAuthHeader(request.method, request.url, request.data));
pm.environment.set('xAmzDate', amzdate);
function getPath(url)
var pathRegex = /.+?\:\/\/.+?(\/.+?)(?:#|\?|$)/;
var result = url.match(pathRegex);
return result && result.length > 1 ? result[1] : '';
function getQueryString(url)
var arrSplit = url.split('?');
return arrSplit.length > 1 ? url.substring(url.indexOf('?') + 1) : '';
function getSignatureKey(secretKey, dateStamp, regionName, serviceName)
var kDate = sign("AWS4" + secretKey, dateStamp);
var kRegion = sign(kDate, regionName);
var kService = sign(kRegion, serviceName);
var kSigning = sign(kService, "aws4_request");
return kSigning;
function sign(key, message)
return CryptoJS.HmacSHA256(message, key);
function getAuthHeader(httpMethod, requestUrl, requestBody)
var ACCESS_KEY = pm.globals.get("access_key");
var SECRET_KEY = pm.globals.get("secret_key");
var REGION = 'us-east-1';
var SERVICE = 'es';
var ALGORITHM = 'AWS4-HMAC-SHA256';
var canonicalUri = getPath(requestUrl);
var canonicalQueryString = getQueryString(requestUrl);
if (httpMethod == 'GET' || !requestBody)
requestBody = '';
else
requestBody = JSON.stringify(requestBody);
var hashedPayload = CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(requestBody));
var canonicalHeaders = 'host:' + pm.environment.get("ESHost") + '\n' + 'x-amz-date:' + amzdate + '\n';
var signedHeaders = 'host;x-amz-date';
var canonicalRequestData = [httpMethod, canonicalUri, canonicalQueryString, canonicalHeaders, signedHeaders, hashedPayload].join("\n");
var hashedRequestData = CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(canonicalRequestData));
var credentialScope = dateStamp + '/' + REGION + '/' + SERVICE + '/' + 'aws4_request';
var stringToSign = ALGORITHM + '\n' + amzdate + '\n' + credentialScope + '\n' + hashedRequestData;
var signingKey = getSignatureKey(SECRET_KEY, dateStamp, REGION, SERVICE);
var signature = CryptoJS.HmacSHA256(stringToSign, signingKey).toString(CryptoJS.enc.Hex);
var authHeader = ALGORITHM + ' ' + 'Credential=' + ACCESS_KEY + '/' + credentialScope + ', ' + 'SignedHeaders=' + signedHeaders + ', ' + 'Signature=' + signature;
return authHeader;
【问题讨论】:
更新:该问题仅适用于 POST 和 PUT 操作。 DELETE 适用于上述预脚本。使用 POST 搜索弹性搜索时与签名不匹配。 【参考方案1】:来自 OP 的代码几乎是准确的,只是有一些错误
1) 当path=''
时getPath应该返回“/”
2) 检查 request.data 是否为空对象requestBody = ''
3)不需要做JSON.stringify(request.data)
,因为request.data返回一个json字符串
固定的sn-p如下:
var date = new Date().toISOString();
var amzdate = date.replace(/[:\-]|\.\d3/g, "");
var dateStamp = amzdate.slice(0, -8);
pm.environment.set('authorization', getAuthHeader(request.method, request.url, request.data));
pm.environment.set('xAmzDate', amzdate);
function getPath(url)
var pathRegex = /.+?\:\/\/.+?(\/.+?)(?:#|\?|$)/;
var result = url.match(pathRegex);
return result && result.length > 1 ? result[1] : '/';
function getQueryString(url)
var arrSplit = url.split('?');
return arrSplit.length > 1 ? url.substring(url.indexOf('?') + 1) : '';
function getSignatureKey(secretKey, dateStamp, regionName, serviceName)
var kDate = sign("AWS4" + secretKey, dateStamp);
var kRegion = sign(kDate, regionName);
var kService = sign(kRegion, serviceName);
var kSigning = sign(kService, "aws4_request");
return kSigning;
function sign(key, message)
return CryptoJS.HmacSHA256(message, key);
function getAuthHeader(httpMethod, requestUrl, requestBody)
var ACCESS_KEY = pm.globals.get("access_key");
var SECRET_KEY = pm.globals.get("secret_key");
var REGION = 'us-east-1';
var SERVICE = 'es';
var ALGORITHM = 'AWS4-HMAC-SHA256';
var canonicalUri = getPath(requestUrl);
var canonicalQueryString = getQueryString(requestUrl);
if (httpMethod == 'GET' || !requestBody || Object.keys(requestBody).length === 0)
requestBody = '';
var hashedPayload = CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(requestBody));
var canonicalHeaders = 'host:' + pm.environment.get("ESHost") + '\n' + 'x-amz-date:' + amzdate + '\n';
var signedHeaders = 'host;x-amz-date';
var canonicalRequestData = [httpMethod, canonicalUri, canonicalQueryString, canonicalHeaders, signedHeaders, hashedPayload].join("\n");
var hashedRequestData = CryptoJS.enc.Hex.stringify(CryptoJS.SHA256(canonicalRequestData));
var credentialScope = dateStamp + '/' + REGION + '/' + SERVICE + '/' + 'aws4_request';
var stringToSign = ALGORITHM + '\n' + amzdate + '\n' + credentialScope + '\n' + hashedRequestData;
var signingKey = getSignatureKey(SECRET_KEY, dateStamp, REGION, SERVICE);
var signature = CryptoJS.HmacSHA256(stringToSign, signingKey).toString(CryptoJS.enc.Hex);
var authHeader = ALGORITHM + ' ' + 'Credential=' + ACCESS_KEY + '/' + credentialScope + ', ' + 'SignedHeaders=' + signedHeaders + ', ' + 'Signature=' + signature;
return authHeader;
【讨论】:
【参考方案2】:在设置 CloudWatch Logs to Amazon Elasticsearch stream 时,AWS 会创建一个 Node.js Lambda 函数,该函数会执行正确的 AWS SigV4 URL 签名。以下是该脚本中的相关部分,您可以重复使用以正确生成邮递员请求:
function buildRequest(endpoint, body)
var endpointParts = endpoint.match(/^([^\.]+)\.?([^\.]*)\.?([^\.]*)\.amazonaws\.com$/);
var region = endpointParts[2];
var service = endpointParts[3];
var datetime = (new Date()).toISOString().replace(/[:\-]|\.\d3/g, '');
var date = datetime.substr(0, 8);
var kDate = hmac('AWS4' + process.env.AWS_SECRET_ACCESS_KEY, date);
var kRegion = hmac(kDate, region);
var kService = hmac(kRegion, service);
var kSigning = hmac(kService, 'aws4_request');
var request =
host: endpoint,
method: 'POST',
path: '/_bulk',
body: body,
headers:
'Content-Type': 'application/json',
'Host': endpoint,
'Content-Length': Buffer.byteLength(body),
'X-Amz-Security-Token': process.env.AWS_SESSION_TOKEN,
'X-Amz-Date': datetime
;
var canonicalHeaders = Object.keys(request.headers)
.sort(function(a, b) return a.toLowerCase() < b.toLowerCase() ? -1 : 1; )
.map(function(k) return k.toLowerCase() + ':' + request.headers[k]; )
.join('\n');
var signedHeaders = Object.keys(request.headers)
.map(function(k) return k.toLowerCase(); )
.sort()
.join(';');
var canonicalString = [
request.method,
request.path, '',
canonicalHeaders, '',
signedHeaders,
hash(request.body, 'hex'),
].join('\n');
var credentialString = [ date, region, service, 'aws4_request' ].join('/');
var stringToSign = [
'AWS4-HMAC-SHA256',
datetime,
credentialString,
hash(canonicalString, 'hex')
] .join('\n');
request.headers.Authorization = [
'AWS4-HMAC-SHA256 Credential=' + process.env.AWS_ACCESS_KEY_ID + '/' + credentialString,
'SignedHeaders=' + signedHeaders,
'Signature=' + hmac(kSigning, stringToSign, 'hex')
].join(', ');
return request;
function hmac(key, str, encoding)
return crypto.createHmac('sha256', key).update(str, 'utf8').digest(encoding);
function hash(str, encoding)
return crypto.createHash('sha256').update(str, 'utf8').digest(encoding);
【讨论】:
我比较了上面和我的,但不知何故我看不出任何区别。你能明确指出问题出在哪里吗?从过去 2 天开始,我一直在努力让它工作。 这可能是很难发现的小东西。如果可以的话,你可能想试试我的功能。它绝对适用于 POST。 我认为我不能像在 Postman 中一样复制您的函数,因为 postman 使用不同的加密函数,除非我遗漏了什么。另外,我注意到您的 canonicalString 包含 7 个参数,这似乎与 AWS 提到的不同,并且只有 6 个参数。那肯定会给我 7 个参数的错误(其中 2 个是空的)。但是,如果您可以在邮递员中复制我的功能,您将能够快速重现该问题。让我知道是否有任何其他方式可以使用您的功能。 我试过你的功能(修改为使用邮递员功能)但没有运气和同样的错误:我想发布修改后的功能但不能在评论中发布,不知道如何在这里发布. 这是我添加的代码的修改版本:pastebin.com/EwFSMPvZ 但它在邮递员中不起作用并导致相同的签名不匹配错误:(如果你能看一下,请先感谢!提前感谢!以上是关于通过 Postman 使用 AWS4 签名进行 CRUD 弹性操作的主要内容,如果未能解决你的问题,请参考以下文章
Woocommerce API Post 方法不适用于通过 POSTMAN 的 HTTP 请求 - 获取无效签名
ap-south-1区域的AWS SignatureDoesNotMatch错误