目录

 

 

Token Auth

Token 认证,即:Client 输入的 username/password 通过 Base Auth 后,Server 颁发授权的 Token(包含 User ID 等基本信息)用作认证令牌,后续 Client 发出的任何请求都要携带 Token 来访问 Server,Server 根据 Token 来判断请求是否合法。

Token Auth 的缺点是,如果报文在中途被劫持,Token 就会泄露,这时(Token 的有效期内)黑客就可以构造任意的请求了。

AK/SK Auth

AK/SK Auth 方式解决了 Token Auth 方式的缺点,因为 AK/SK Auth 的底层逻辑是基于对称加密的。

AK/SK 的本质是一组认证证书:

  • AK(Access Key ID,接入键标识):唯一标识一个 Client。
  • SK(Secret Access Key,安全接入键):AK 与 SK 一一对应,Client 或 Server 都会将 “SK + Request Body” 通过加密运算来生成一个安全密钥(Signature,签名)。SK 必须保密。

AK/SK 认证,一般用于后台程序执行 API 调用时的 Server 认证。AK 标识 Client,SK 作为对称加密通信的秘钥。

  • Client:构建 HTTP 请求,加密运算 Request Body 和 SK 得到 Signature,将 AK 和 Signature 发送请求到 Server。
  • Server:根据 Client 发送的 AK 查找数据库得到对应的 SK,使用同样的加密算法将 Request Body 和 SK 一起计算得到 Signature,对比 Client 发送而来的 Signature 和 Server 计算得到的 Signature,两者相同则认证通过,否则失败。

可见,AK/SK Auth 要求 Client 和 Server 都保存有相同的 AK/SK 信息。

AK/SK Auth 实现原理

下面以华为云为例。

Step 1:获取 AK/SK

AK/SK 的生成步骤:

  1. 注册并登录管理控制台。
  2. 单击右上角的用户名,在下拉列表中单击 “我的凭证”。
  3. 单击 “访问密钥”。
  4. 单击 “新增访问密钥”,进入 “新增访问密钥” 页面。
  5. 输入登录密码和短信验证码,单击 “确定”,下载密钥,请妥善保管。

Step 2:构造一个 HTTP 规范请求(CanonicalRequest)

一个 HTTP CanonicalRequest 的格式如下:

CanonicalRequest =
      HTTPRequestMethod + '\n' +         # e.g. GET、POST、PUT、DELETE
      CanonicalURI + '\n' +              # e.g. /、/service_uri
      CanonicalQueryString + '\n' +      # e.g. ?parm1=value1&parm2=
      CanonicalHeaders + '\n' +          # e.g. content-type、host、x-sdk-date
      SignedHeaders + '\n' +
      HexEncode(Hash(RequestPayload))

其中,CanonicalHeaders 必须包含 X-Sdk-Date,用于校验签名时间,格式为 ISO8601 标准的 UTC 时间格式:YYYYMMDDTHHMMSSZ。这意味着,Client 和 Server 必须注意时钟同步服务,避免 X-Sdk-Date 的值出现较大误差。APIGW 除了校验时间格式外,还会校验该时间值与网关收到请求的时间差,如果时间差超过 15 分钟,APIGW 将拒绝请求。

另外,SignedHeaders 是 AK/SK 最重要的部分,它指定了用于进行签名的 HTTP Headers 列表。通过 SignedHeaders,向 APIGW 告知 HTTP Request 中哪些 Headers 需要参与签名,以及在验证请求时 APIGW 可以忽略哪些 Headers。

例如:下述表示 HTTP Headers content-type;host;x-sdk-date 这三个消息头的信息需要作为 RequestPayload 参与签名,其中的 X-Sdk-date 则必须参与签名。

SignedHeaders=content-type;host;x-sdk-date

指定了 SignedHeaders 之后,则需要对其中指定的 RequestPayload 做一次 HASH 计算,并得到一个字符串,这个字符串称之为 “信息摘要”,具有哈希性。计算的公式为:

HexEncode(Hash(RequestPayload))
  • Hash:表示生成消息摘要的函数,当前支持 SHA-256 算法。
  • HexEncode:表示以小写字母形式返回摘要的 Base-16 编码的函数。例如:HexEncode(“m”) 返回值为 “6d” 而不是 “6D”。

例如:SignedHeaders 指定了 content-type;host;x-sdk-date 这三个 CanonicalHeaders 的消息必须参与签名,那么下列消息可以得到的签名为:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

content-type:application/json
host:service.region.example.com
x-sdk-date:20191115T033655Z

至此,我们得到了一个 HTTP CanonicalRequest:

GET                                                       # HTTPRequestMethod
/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs/                # CanonicalURI
limit=2&marker=13551d6b-755d-4757-b956-536f674975c0       # CanonicalQueryString
content-type:application/json                             # CanonicalHeader 0
host:service.region.example.com                           # CanonicalHeader 1
x-sdk-date:20191115T033655Z                               # CanonicalHeader 2
content-type;host;x-sdk-date                              # SignedHeaders
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855    # HASH

最后,我们需要对构造好的 CanonicalRequest 进行哈希处理,得到 HashedCanonicalRequest 字符串,计算公式:

Lowercase(HexEncode(Hash.SHA256(CanonicalRequest)))
# b25362e603ee30f4f25e7858e8a7160fd36e803bb2dfe206278659d71a9bcd7a

Step 3:基于 HashedCanonicalRequest 构造一个待签字符串(StringToSign)

一个 StringToSign 的格式如下:

StringToSign =
    Algorithm + \n +           # 指定签名算法。对于 SHA256,算法为 SDK-HMAC-SHA256。
    RequestDateTime + \n +     # 指定请求时间戳。
    HashedCanonicalRequest     # 指定 CanonicalRequest。

例子:

SDK-HMAC-SHA256
20191115T033655Z
b25362e603ee30f4f25e7858e8a7160fd36e803bb2dfe206278659d71a9bcd7a

Step 4:基于 SK 和 StringToSign 计算出签名

至此,我们得到了通过 Step 1 得到了 SK 字符串,然后通过 Step 3 得到了 StringToSign,现在我们就可以进行签名计算了,计算公式为:

signature = HexEncode(HMAC(SK, StringToSign))
# 7be6668032f70418fcc22abc52071e57aff61b84a1d2381bb430d6870f4f6ebe

Step 5:发起 AK/SK HTTP Request

在计算出 Signature 后,就可以将它添加到 HTTP Header Authorization 了,Authorization Header 主要用于身份验证。格式如下:

Authorization: algorithm Access=Access key, SignedHeaders=SignedHeaders, Signature=signature
# algorithm:签名算法。
# Access:AK 字符串。
# SignedHeaders:参与签名的 HTTP Header。
# Signature:签名字符串。

例如:

Authorization: SDK-HMAC-SHA256 Access=QTWAOYTTINDUT2QVKYUC, SignedHeaders=content-type;host;x-sdk-date, Signature=7be6668032f70418fcc22abc52071e57aff61b84a1d2381bb430d6870f4f6ebe

完整的 AK/SK HTTP Request 为:

curl -X GET "https://service.region.example.com/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs?limit=2&marker=13551d6b-755d-4757-b956-536f674975c0" \
-H "content-type: application/json" \
-H "X-Sdk-Date: 20191115T033655Z" \
-H "host: service.region.example.com" \
-H "Authorization: SDK-HMAC-SHA256 Access=QTWAOYTTINDUT2QVKYUC, SignedHeaders=content-type;host;x-sdk-date, Signature=7be6668032f70418fcc22abc52071e57aff61b84a1d2381bb430d6870f4f6ebe" \
-d $''

注:通常的,客户端会发起 AK/SK Token Request,先获取 Token 然后再进行后续的请求。