为保证用户日志数据的安全,日志服务API的所有HTTP请求都必须经过安全验证。

验证流程

安全验证基于阿里云的访问密钥,使用对称加密算法完成。

验证流程图如下:全验证流程

验证操作流程如下:

  1. 请求端根据API请求内容(包括HTTP Header和Body)生成签名字符串。
  2. 请求端使用阿里云的访问密钥对(AccessKeyID和AccessKeySecret),对步骤1生成的签名字符串进行签名,形成该API请求的数字签名。
  3. 请求端把API请求内容和数字签名一同发送给服务端。
  4. 服务端在接到请求后会重复如上的步骤1和步骤2的工作,并在服务端计算出该请求期望的数字签名。
    说明 服务端会在后台取得该请求使用的用户访问密钥对。
  5. 服务端用期望的数字签名和请求端发送过来的数字签名做比对,如果一致则认为该请求通过安全验证,否则直接拒绝该请求。
安全验证能达到以下目的:
  • 确认哪位用户在做API请求。

    因为在发送请求前需要用户指定生成数字签名的密钥对,在服务端即可通过该密钥对确定用户身份,进而可做访问权限管理。

  • 确认用户请求在网络传输过程中是否被篡改。

    因为服务端会对接收到的请求内容重新计算数字签名,若请求内容在网络上被篡改,则无法通过数字签名比对。

签名API请求

为了通过API请求的安全验证,用户需要在客户端对其API请求进行签名(即生成正确的数字签名),并且使用HTTP头Authorization在网络上传输该请求的数字签名。Authorization头示例如下:
Authorization = "LOG" + AccessKeyId + ":" + Signature

如上格式所示,Authorization头的值包含用户访问密钥对中的AccessKeyId,且与之对应的AccessKeySecret将用于Signature值的构造。下面将详细解释如何构造该Signature值。

  1. 准备阿里云访问密钥。
    为API请求生成签名,需使用一对访问密钥。您可以使用已经存在的访问密钥对,也可以创建新的访问密钥对,但需要保证使用的密钥对为启用状态。
  2. 生成请求的签名字符串。
    日志服务API的签名字符串由HTTP请求中的Method、Header和Body信息一同生成,具体方式如下:
    SignString = VERB + "\n"
                 + CONTENT-MD5 + "\n"
                 + CONTENT-TYPE + "\n"
                 + DATE + "\n"
                 + CanonicalizedLOGHeaders + "\n"
                 + CanonicalizedResource
    上面公式中的\n表示换行转义字符,加号(+)表示字符串连接操作,其他各个部分定义如下所示。
    表 1. 签名字符串定义
    名称 说明 示例
    VERB HTTP请求的方法名称。 PUT、GET、POST等
    CONTENT-MD5 HTTP请求中Body部分的MD5值(必须为大写字符串)。 875264590688CA6171F6228AF5BBB3D2
    CONTENT-TYPE HTTP请求中Body部分的类型。 application/x-protobuf
    DATE HTTP请求中的标准时间戳头(遵循RFC 1123格式,使用GMT标准时间)。 Mon,3 Jan 2010 08:33:47 GMT
    CanonicalizedLOGHeaders 由HTTP请求中以x-logx-acs为前缀的自定义头构造的字符串(具体构造方法见下面详述)。 x-log-apiversion:0.6.0\nx-log-bodyrawsize:50\nx-log-signaturemethod:hmac-sha1
    CanonicalizedResource 由HTTP请求资源构造的字符串(具体构造方法见下面详述)。 /logstores/app_log
    CONTENT-TYPE
    对于部分无Body的HTTP请求,其CONTENT-MD5CONTENT-TYPE两个域为空字符串,这时整个签名字符串的生成方式如下:
    SignString = VERB + "\n"
                 + "\n"
                 + "\n"
                 + DATE + "\n"
                 + CanonicalizedLOGHeaders + "\n"
                 + CanonicalizedResource
    公共请求头中的描述,日志服务API中引入了一个自定义请求头x-log-date。如果您在请求中指定了该请求头,则其值会替代HTTP标准请求头Date加入签名计算。
    • CanonicalizedLOGHeaders的构造方式如下:
      1. 将所有以x-logx-acs为前缀的HTTP请求头的名字转换成小写字母。
      2. 将上一步得到的所有LOG自定义请求头按照字典顺序进行升序排序。
      3. 删除请求头和内容之间分隔符两端出现的任何空格。
      4. 将所有的头和内容用\n分隔符组合成最后的CanonicalizedLOGHeader。
    • CanonicalizedResource的构造方式如下:
      1. 将CanonicalizedResource设置为空字符串" "
      2. 放入要访问的LOG资源,如/logstores/logstorename(如果没有logstorename则可不填写)。
      3. 如果请求包含查询字符串QUERY_STRING,则在CanonicalizedResource字符串尾部添加?和查询字符串。
      QUERY_STRING是URL中请求参数按字典顺序排序后的字符串,其中参数名和值之间用=相隔组成字符串,并对参数名-值对按照字典顺序升序排序,然后以&符号连接构成字符串。其公式化描述如下:
      QUERY_STRING = "KEY1=VALUE1" + "&" + "KEY2=VALUE2"
  3. 生成请求的数字签名。
    目前,日志服务API只支持一种数字签名算法,即默认签名算法hmac-sha1。其完整签名公式如下:
    Signature = base64(hmac-sha1(UTF8-Encoding-Of(SignString),AccessKeySecret))
    • 签名的方法用RFC 2104中定义的HMAC-SHA1方法。如上公式用的AccessKeySecret必须和最终的Authorization头中使用的AccessKeyId相对应。否则,请求将无法通过服务端验证。
    • 在计算出数字签名后,使用该值按本节最前面描述的Authorization头格式构建完整的Log Service API请求安全验证头,并填入HTTP请求中即可发送。
    • 签名的字符串必须为UTF-8格式。含有中文字符的签名字符串必须先进行UTF-8编码,再与AccessKeySecret计算最终签名。
    • CONTENT-MD5CONTENT-TYPE在请求中不是必须的,但是如果请求需要签名验证,空值的话以换行符\n代替。

请求签名过程示例

为了帮助您更好地理解整个请求签名的流程,我们用两个示例来演示整个过程。首先,假设您用做日志服务API签名的访问密钥对如下:
AccessKeyId = "bq2sjzesjmo86kq*********"
AccessKeySecret = "4fdO2fTDDnZPU/L7CHNd********"
  • 示例一
    您需要发送如下GET请求列出ali-test-project项目下的所有Logstore,其HTTP请求如下:
    GET /logstores HTTP 1.1
    Mon, 09 Nov 2015 06:11:16 GMT
    Host: ali-test-project.regionid.example.com
    x-log-apiversion: 0.6.0
    x-log-signaturemethod: hmac-sha1
    如上Log Service API请求生成的签名字符串为:
    GET\n\n\nMon, 09 Nov 2015 06:11:16 GMT\nx-log-apiversion:0.6.0\nx-log-signaturemethod:hmac-sha1\n/logstores?logstoreName=&offset=0&size=1000
    由于是GET请求,该请求无任何HTTP Body,所以生成的签名字符串中CONTENT-TYPE与CONTENT-MD5域为空字符串。如果以前面指定的AccessKeySecret做签名运算后得到的签名为:
    jEYOTCJs2e88o+y5F4/S5IsnBJQ=
    最后发送经数字签名的HTTP请求内容如下:
    GET /logstores HTTP 1.1
    Mon, 09 Nov 2015 06:11:16 GMT
    Host: ali-test-project.regionid.example.com
    x-log-apiversion: 0.6.0
    x-log-signaturemethod: hmac-sha1
    Authorization: LOG bq2sjzesjmo86kq35behupbq:jEYOTCJs2e88o+y5F4/S5IsnBJQ=
  • 示例二
    您需要给同上例ali-test-project项目中名为test-logstore的Logstore写入下面的日志:
    topic=""
    time=1447048976
    source="10.10.10.1"
    "TestKey": "TestContent"
    为此,按照Log Service API定义需要构建如下HTTP请求:
    POST /logstores/test-logstore HTTP/1.1
    Date: Mon, 09 Nov 2015 06:03:03 GMT
    Host: test-project.regionid.example.com
    x-log-apiversion: 0.6.0
    x-log-signaturemethod: hmac-sha1
    Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
    Content-Length: 52
    x-log-apiversion:0.6.0
    x-log-bodyrawsize:50
    x-log-compresstype:lz4
    x-log-signaturemethod:hmac-sha1
    <日志内容序列化成ProtoBuffer格式的字节流>
    在这个HTTP请求中,写入的日志内容首先被序列化成ProtoBuffer格式(请参见数据编码方式了解该格式的更多细节)后作为请求Body。所以该请求的Content-Type头的值指定为application/x-protobuf。类似,Content-MD5头的值是请求body对应的MD5值。按照上面的签名字符串构造方式,这个请求对应的签名字符串为:
    POST\n1DD45FA4A70A9300CC9FE7305AF2C494\napplication/x-protobuf\nMon, 09 Nov 2015 06:03:03 GMT\nx-log-apiversion:0.6.0\nx-log-bodyrawsize:50\nx-log-compresstype:lz4\nx-log-signaturemethod:hmac-sha1\n/logstores/test-logstore
    同样,以前面示例中的AccessKeySecret做签名运算,得到的最终签名为:
    XWLGYHGg2F2hcfxWxMLiNkGki6g=
    最后发送经数字签名的HTTP请求内容如下:
    POST /logstores/test-logstore HTTP/1.1
    Date: Mon, 09 Nov 2015 06:03:03 GMT
    Host: test-project.regionid.example.com
    x-log-apiversion: 0.6.0
    x-log-signaturemethod: hmac-sha1
    Content-MD5: 1DD45FA4A70A9300CC9FE7305AF2C494
    Content-Length: 52
    x-log-apiversion:0.6.0
    x-log-bodyrawsize:50
    x-log-compresstype:lz4
    x-log-signaturemethod:hmac-sha1
    Authorization: LOG bq2sjzesjmo86kq35behupbq:XWLGYHGg2F2hcfxWxMLiNkGki6g=
    <日志内容序列化成ProtoBuffer格式的字节流>