背景

由于项目需要,微服务的API都是从AWS API Gateway发布的,然后给第三方系统去调用,比如小程序等。

AWS提供了很详细的文档来生成签名,同时也可以生成各种编程语言的SDK来简化签名。

本文记录了使用nodejs如何一步一步去实现这个验签过程的。

API Gateway方法请求保护

AWS API Gateway是AWS的一项全托管服务,可以用来发布管理保护监控API。保护API通常在方法请求中设置两种验证方式:

  • AWS_IAM
  • API Key

具体设置如下:
AWS API Gateway 签名验证示例 - 图1

API Key验证

这个非常简单,只需要在请求的http header里面添加一行

  1. x-api-key: <your api key>

AWS_IAM 计算签名

API Gateway支持AWS Signature V4, 简单来讲就是对于向AWS发起的每个http请求都集成AKSK作为安全凭证。

计算签名步骤一共有4步:

  1. 创建规范请求
  2. 创建签名字符串
  3. 计算签名
  4. 添加签名到http header

创建规范请求

针对签名版本 4 创建规范请求

这里需要注意的是对于payload生成一个hash值。使用SHA256指定AWS4-HMAC-SHA256 作为签名算法。经过哈希处理的负载必须以小写十六进制(hex)字符串形式表示。

const hash = (string, encoding) => {
  return crypto
    .createHash('sha256')
    .update(string, 'utf8')
    .digest(encoding);
};

另外需要注意的是在signed header前需要添加一个\n换行符

const createCanonicalRequest = request => {
  const canonicalRequest = [
    request.method,
    request.path,
    request.query,
    canonicalHeaders(request.headers) + '\n',
    signedHeaders(request.headers),
    hash(request.data, 'hex')
  ].join('\n');

  console.log('========Canonical Request============');
  console.log(canonicalRequest);
  console.log('========Canonical Request============\n');

  return canonicalRequest;
};

创建签名字符串

创建签名版本 4 的待签字符串

这一步需要注意的是x-amz-date时间是UTC时间。

const createStringToSign = (options, request) => {
  const stringToSign = [
    'AWS4-HMAC-SHA256',
    getDateTime(),
    getDate() + `/${options.region}/${options.service}/aws4_request`,
    hash(createCanonicalRequest(request), 'hex')
  ].join('\n');

  console.log('========String to sign============');
  console.log(stringToSign);
  console.log('========String to sign============\n');
  return stringToSign;
};

计算签名

计算签名

根据文档的伪代码去嵌套计算签名,时间仍然为UTC时间。

kSecret = your secret access key
kDate = HMAC("AWS4" + kSecret, Date)
kRegion = HMAC(kDate, Region)
kService = HMAC(kRegion, Service)
kSigning = HMAC(kService, "aws4_request")
const calculateSignature = (options, request) => {
  const kSecret = options.secretAccessKey;
  const kDate = hmac('AWS4' + kSecret, getDate());
  const kRegion = hmac(kDate, options.region);
  const kService = hmac(kRegion, options.service);
  const kSigning = hmac(kService, 'aws4_request');
  const singature = hmac(kSigning, createStringToSign(options, request), 'hex');

  console.log('========Authorization============');
  console.log(singature);
  console.log('========Authorization============\n');
  return singature;
};

添加签名

向 HTTP 请求添加签名

const calculateAuthorization = (options, request) => {
  return `AWS4-HMAC-SHA256 Credential=${options.accessKeyId}/${getDate()}/${
    options.region
  }/${options.service}/aws4_request, SignedHeaders=${signedHeaders(
    request.headers
  )}, Signature=${calculateSignature(options, request)}`;
};

Github源代码以及用例