全部产品
弹性计算 会员服务 网络 安全 移动云 数加·大数据分析及展现 数加·大数据应用 管理与监控 云通信 阿里云办公 培训与认证 智能硬件
存储与CDN 数据库 域名与网站(万网) 应用服务 数加·人工智能 数加·大数据基础服务 互联网中间件 视频服务 开发者工具 解决方案 物联网 更多
函数计算

签名认证

更新时间:2018-07-05 10:55:27

对于每一个请求,函数计算服务会根据请求头部的 Authorization 字段来校验是否合法(设置了 HTTP 触发器的允许匿名访问的函数除外)。客户端须使用与函数计算服务端一致的签名算法才能通过验证,对于未包含签名字段或者签名错误的请求,函数计算服务将会返回 HTTP 403 错误。

本文对函数计算的签名校验算法进行介绍。

签名算法

  1. signature = base64(hmac-sha256(HTTP_METHOD + "\n"
  2. + CONTENT-MD5 + "\n"
  3. + CONTENT-TYPE + "\n"
  4. + DATE + "\n"
  5. + CanonicalizedFCHeaders
  6. + CanonicalizedResource))
  7. // Authorization字段介绍
  8. Authorization = "FC " + accessKeyID + ":" + signature
  • HTTP_METHOD 表示大写的 HTTP Method (如:PUT, GET, POST, DELETE)
  • CONTENT-MD5 表示请求内容数据的 MD5 值。如果请求的 Header 中没有传 Content-MD5,则此处填入空串
  • CONTENT-TYPE 表示请求内容的类型
  • DATE 表示此次操作的时间,不能为空,目前只支持 GMT 格式
    • 注意:客户端需要保证生成的时间与函数计算服务端的时间相差不超过15分钟,否则函数服务将拒绝此请求
  • CanonicalizedFCHeaders 表示所有以 x-fc- 为前缀的 HTTP 头组成的字符串,生成方式见下文
  • CanonicalizedResource 表示请求的 URL 的 Path ,一般来说是先对收到的 Path decode,再去掉请求的 Path 里的 Params 内容。
    • Path 的结构为:$api-version/api-path
      • api-version:API 版本,当前版本为 2016-08-15
      • api-path:访问各个接口的路径,例如创建 service 为 /services,其他 path 请参考 API 定义
    • 需要认证的 HTTP 触发器的CanonicalizedResource 与其他请求的CanonicalizedResource 不同,下面对两种情况分别进行介绍。
      • 普通请求的 CanonicalizedResource (普通请求为除了需要访问带认证的 HTTP 触发器的请求外的所有请求): 首先对收到的 Path 进行 url 解码,普通请求的 CanonicalizedResource 会只取到?前面的内容,即舍弃传入的各个Params
      • 需要认证的 HTTP 触发器的CanonicalizedResource :如果有 Params ,则以回车符 \n 分隔各个参数,Params 中的各个参数 key - value 对按照字母序进行排序(如果 Params 里的 key 对应多个 value ,即对 key - value 整体进行排序)。如果没有 Params,最后也以 \n 结束。例如
  1. // 需要认证的 HTTP 触发器的 url 的真实 path
  2. /2016-08-15/proxy/service-name/func-name/path-with-%20-space/action?x=1&a=2&x=3&with%20space=foo%20bar
  3. // url decode 后的结果
  4. /2016-08-15/proxy/service-name/func-name/path-with- -space/action?x=1&a=2&x=3&with space=foo bar
  5. // 需要认证的 HTTP 触发器的 CanonicalizedResource
  6. /2016-08-15/proxy/service-name/func-name/path-with- -space/action\na=2\nwith space=foo bar\nx=1\nx=3
  7. // 普通请求的 url 的真实 path
  8. /2016-08-15/service-name/func-name/path-with-%20-space/action?x=1&a=2&x=3&with%20space=foo%20bar
  9. // url decode 后的结果
  10. /2016-08-15/service-name/func-name/path-with- -space/action?x=1&a=2&x=3&with space=foo bar
  11. // 普通请求的 CanonicalizedResource
  12. /2016-08-15/service-name/func-name/path-with- -space/action
  • hmac-sha256需要以用户的 AccessKeySecret 为 Key

伪代码如下:

  1. // 构造字符串的过程
  2. function composeStringToSign(method, path, headers, queries) {
  3. var contentMD5 = headers['content-md5'] || '';
  4. var contentType = headers['content-type'] || '';
  5. var date = headers['date'];
  6. var signHeaders = buildCanonicalHeaders(headers, 'x-fc-');
  7. var u = url.parse(path);
  8. var pathUnescaped = decodeURIComponent(u.pathname);
  9. var str = `${method}\n${contentMD5}\n${contentType}\n${date}\n${signHeaders}${pathUnescaped}`;
  10. if (queries) {
  11. var params = [];
  12. Object.keys(queries).forEach(function (key) {
  13. var values = queries[key];
  14. var type = typeof values;
  15. if (type === 'string') {
  16. params.push(`${key}=${values}`);
  17. return;
  18. }
  19. if (type === 'object' && values instanceof Array) {
  20. queries[key].forEach(function (value) {
  21. params.push(`${key}=${value}`);
  22. });
  23. }
  24. });
  25. params.sort();
  26. str += '\n' + params.join('\n');
  27. }
  28. return str;
  29. }
  30. // 使用 hmac-sha256 和 base64 计算签名的过程,其中 source 参数为构造出的字符串
  31. function signString(source, secret) {
  32. const buff = crypto.createHmac('sha256', secret)
  33. .update(source, 'utf8')
  34. .digest();
  35. return new Buffer(buff, 'binary').toString('base64');
  36. }

CanonicalizedFCHeaders

生成步骤如下:

  1. 找出请求头中所有以 x-fc- 开头的字段(不区分大小写)
    • 对于符合前缀的字段,先将字段名转换成小写
  2. 对于每一个字段,生成一个子串 ${key}:${value}\n
    • ${key} 是 HTTP 头的名称(转换成小写)
    • ${value} 是 HTTP 头的值
    • 例如:X-Fc-Invocation-Type:Sync 变成 x-fc-invocation-type:Sync\n
    • 然后将这些字段按字段名从小到大排序
  3. 将上述生成的子串连接成一个整串

伪代码如下:

  1. // javascript
  2. // prefix = 'x-fc-'
  3. function buildCanonicalHeaders(headers, prefix) {
  4. var list = [];
  5. var keys = Object.keys(headers);
  6. var fcHeaders = {};
  7. for (var i = 0; i < keys.length; i++) {
  8. var key = keys[i];
  9. var lowerKey = key.toLowerCase().trim();
  10. if (lowerKey.startsWith(prefix)) {
  11. list.push(lowerKey);
  12. fcHeaders[lowerKey] = headers[key];
  13. }
  14. }
  15. list.sort();
  16. var canonical = '';
  17. for (var _i = 0; _i < list.length; _i++) {
  18. var _key = list[_i];
  19. canonical += `${_key}:${fcHeaders[_key]}\n`;
  20. }
  21. return canonical;
  22. }

Authorization 字段

Authorization 可由上文计算得出的 signature 构造出来,构造方法如下:

  1. Authorization = "FC " + accessKeyID + ":" + signature

请求示例

请求:

  1. GET /2016-08-15/services?limit=100&nextToken=&prefix=&startKey= HTTP/1.1
  2. Host: 1237050315505682.fc.cn-shanghai.aliyuncs.com
  3. User-Agent: go-sdk-0.1
  4. Accept: application/json
  5. Authorization: FC LTAIUyt0Yeq1rgqo:GBmoz6OwC7bobTlD1jboBZ9PkaZ1e4cKsQ+5/dlLTns=
  6. Date: Mon, 08 May 2017 03:08:31 GMT
  7. X-User-Agent: go-resty v0.11 - https://github.com/go-resty/resty
  8. Accept-Encoding: gzip

响应:

  1. HTTP/1.1 200 OK
  2. Content-Type: application/json; charset=utf-8
  3. X-Fc-Request-Id: ab7c7602-0922-f04f-b4ee-923cd7df7fb0
  4. Date: Mon, 08 May 2017 03:08:31 GMT
  5. Transfer-Encoding: chunked

代码示例

可以参考我们已经发布的SDK中签名部分的代码:

本文导读目录