4. 访问控制
4.1 用户签名验证(Authentication)
OSS通过使用Access Key ID/ Access Key Secret对称加密的方法来验证某个请求的发送者身份。Access Key ID用于标示用户,Access Key Secret是用户用于加密签名字符串和OSS用来验证签名字符串的密钥,其中Access Key Secret必须保密,只有用户和OSS知道。每个ACCESS Key对(Access Key ID和Access Key Secret)都有active/inactive两种状态。
n active表明用户可以用此ID对签名验证请求
n inactive表明用户暂停此ID对签名验证的功能
一个用户可以同时拥有0至2个active或者inactive的ID对。用户可以登录http://oss.aliyun.com/,在OSS管理控制台申请新增或删除ID对。
当用户想以个人身份向OSS发送请求时,需要首先将发送的请求按照OSS指定的格式生成签名字符串;然后使用Access Key Secret对签名字符串进行加密产生验证码。 OSS收到请求以后,会通过Access Key ID找到对应的Access Key Secret,以同样的方法提取签名字符串和验证码,如果计算出来的验证码和提供的一样即认为该请求是有效的;否则,OSS将拒绝处理这次请求,并返回HTTP 403错误。
4.2 在Head中包含签名
用户可以在HTTP请求中增加Authorization(授权)的Head来包含签名(Signature)信息,表明这个消息已被授权。
Authorization字段计算方法如下:
"Authorization: OSS " + Access Key ID + ":" + Signature
Signature = base64(hmac-sha1(VERB + "\n"
+CONTENT-MD5 + "\n"
+CONTENT-TYPE + "\n"
+DATE + "\n"
+CanonicalizedOSSHeaders
+CanonicalizedResource))
n CONTENT-MD5表示请求内容数据的MD5值
n CONTENT-TYPE表示请求内容的类型
n DATE表示此次操作的时间,且必须为HTTP1.1中支持的GMT格式
n CanonicalizedOSSHeaders表示 http中的object meta组合
n CanonicalizedResource 表示用户想要访问的OSS资源
其中,DATE和CanonicalizedResource不能为空;如果请求中的DATE时间和OSS服务器的时间差正负15分钟以上,OSS服务器将拒绝该服务,并返回HTTP 403错误。
构建CanonicalizedOSSHeaders的方法:
所有以“x-oss-”为前缀的HTTP Header被称为CanonicalizedOSSHeaders。它的构建方法如下:
1) 将所有以“x-oss-”为前缀的HTTP请求头的名字转换成小写字母。如’X-OSS-Meta-Name: TaoBao’转换成’x-oss-meta-name: TaoBao’。 2) 将上一步得到的所有HTTP请求头按照字典序进行升序排列。 3) 如果有相同名字的请求头,则根据标准RFC 2616, 4.2章进行合并(两个值之间只用逗号分隔)。如有两个名为’x-oss-meta-name’的请求头,对应的值分别为’TaoBao’和’Alipay’,则合并后为:’x-oss-meta-name:TaoBao,Alipay’。 4) 删除请求头和内容之间分隔符两端出现的任何空格。如’x-oss-meta-name: TaoBao,Alipay’转换成:’x-oss-meta-name:TaoBao,Alipay’。 5) 将所有的头和内容用’\n’分隔符分隔拼成最后的CanonicalizedOSSHeader。 |
构建CanonicalizedResource的方法:
用户发送请求中想访问的OSS目标资源被称为CanonicalizedResource。它的构建方法如下:
1) 将CanonicalizedResource置成空字符串(“”); 2) 放入要访问的OSS资源:“ /BucketName/ObjectName”(无ObjectName则不填) 3) 如果请求的资源包括子资源(sub-resource)[3],那么将所有的子资源按照字典序,从小到大排列并以’&’为分隔符生成子资源字符串。在CanonicalizedResource字符串尾添加“?”和子资源字符串。此时的CanonicalizedResource例子如:/BucketName/ObjectName?acl &uploadId=UploadId 4) 如果用户请求在查询字符串(query string)中指定了要重写(override)返回请求的header[4],那么将这些查询字符串及其请求值按照字典序,从小到大排列,以’&’为分隔符,按参数的字典序添加到CanonicalizedResource中。此时的CanonicalizedResource例子: /BucketName/ObjectName?acl&response-content-type=ContentType & uploadId =UploadId |
例如:想签名以下信息:
PUT /nelson HTTP/1.0 Content-Md5: c8fdb181845a4ca6b8fec737b3581d76 Content-Type: text/html Date: Thu, 17 Nov 2005 18:49:58 GMT Host: oss-example.oss.aliyuncs.com X-OSS-Meta-Author: foo@bar.com X-OSS-Magic: abracadabra |
假如Access Key ID是: "44CF9590006BF252F707"
Access Key Secret是 "OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV",可用以下方法签名(Signature):
python示例代码:
import base64 import hmac import sha h = hmac.new("OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV", "PUT\nc8fdb181845a4ca6b8fec737b3581d76\ntext/html\nThu, 17 Nov 2005 18:49:58 GMT\nx-oss-magic:abracadabra\nx-oss-meta-author:foo@bar.com\n/oss-example/nelson", sha) base64.encodestring(h.digest()).strip() |
签名(Signature)计算结果应该为”63mwfl+zYIOG6k95yxbgMruQ6QI=”, 然后加上Authorization头来组成最后需要发送的消息:
PUT /nelson HTTP/1.0 Authorization: OSS 44CF9590006BF252F707: 63mwfl+zYIOG6k95yxbgMruQ6QI= Content-Md5: c8fdb181845a4ca6b8fec737b3581d76 Content-Type: text/html Date: Thu, 17 Nov 2005 18:49:58 GMT Host: oss-example.oss.aliyuncs.com X-OSS-Meta-Author: foo@bar.com X-OSS-Magic: abracadabra |
在计算签名头的时候请遵循如下规则:
1) 用来签名的字符串必须为UTF-8格式。含有中文字符的签名字符串必须先进行UTF-8编码,再与Access Key Secret计算最终签名。
2) 签名的方法用RFC 2104 (http://www.ietf.org/rfc/rfc2104.txt)中定义的HMAC-SHA1方法,其中Key为Access Key Secret。
3) content-type和content-md5在请求中不是必须的,但是如果请求需要签名验证,空值的话以换行符“\n”代替。
4) 在所有非HTTP标准定义的header中,只有以“x-oss-”开头的header,需要加入签名字符串;其他非HTTP标准header将被OSS忽略(如上例中的x-oss-magic)。
5) 以“x-oss-”开头的head在签名验证前需要符合以下规范:
Ø head的名字需要变成小写。
Ø head按字典序自小到大排序。
Ø 分割head name和value的冒号前后不能有空格。
Ø 每个Head之后都有一个换行符“\n”,如果没有Head,CanonicalizedOSSHeaders就设置为空。
细节分析:
1) 如果传入的Access Key ID不存在或inactive,返回403 Forbidden。错误码:InvalidAccessKeyId。
2) 若用户请求头中Authorization值的格式不对,返回400 Bad Request。错误码:InvalidArgument。
3) OSS所有的请求都必须使用HTTP 1.1协议规定的GMT时间格式。其中,日期的格式有三种:
date1 = 2DIGIT SP month SP 4DIGIT; day month year (e.g., 02 Jun 1982) |
date2 = 2DIGIT "-" month "-" 2DIGIT; day-month-year (e.g., 02-Jun-82) |
date3 = month SP ( 2DIGIT | ( SP 1DIGIT )); month day (e.g., Jun 2) 【注意“2”前面有两个空格】 |
上述这三种日期格式中,“天”所占位数都是“2 DIGIT”。因此,“Jun 2”、“2 Jun 1982”和“2-Jun-82”都是非法日期格式。
4) 如果签名验证的时候,头中没有传入Date或者格式不正确,返回403 Forbidden错误。错误码:AccessDenied。
5) 传入请求的时间必须在OSS服务器当前时间之后的15分钟以内,否则返回403 Forbidden。错误码:RequestTimeTooSkewed。
6) 如果Access Key ID是active的,但OSS判断用户的请求发生签名错误,则返回403 Forbidden,并在返回给用户的response中告诉用户正确的用于验证加密的签名字符串。用户可以根据OSS的response来检查自己的签名字符串是否正确。
返回示例:
<?xml version="1.0" encoding="UTF-8"?> <Error xmlns=”http://doc.oss.aliyuncs.com”> <Code> SignatureDoesNotMatch </Code> <Message> The request signature we calculated does not match the signature you provided. Check your key and signing method. </Message> <StringToSignBytes> 47 45 54 0a 0a 0a 57 65 64 2c 20 31 31 20 4d 61 79 20 32 30 31 31 20 30 37 3a 35 39 3a 32 35 20 47 4d 54 0a 2f 75 73 72 65 61 6c 74 65 73 74 3f 61 63 6c </StringToSignBytes> <RequestId> 1E446260FF9B10C2 </RequestId> <HostId> oss.aliyuncs.com </HostId> <SignatureProvided> y5H7yzPsA/tP4+0tH1HHvPEwUv8= </SignatureProvided> <StringToSign> GET Wed, 11 May 2011 07:59:25 GMT /oss-example?acl </StringToSign> <OSSAccessKeyId> AKIAIVAKMSMOY7VOMRWQ </OSSAccessKeyId> </Error> |
4.3 在URL中包含签名
除了使用Authorization Head,用户还可以在URL中加入签名信息,这样用户就可以把该URL转给第三方实现授权访问。
URL中包含签名示例:
http://oss-example.oss.aliyuncs.com/oss-api.pdf?OSSAccessKeyId=44CF9590006BF252F707&Expires=1141889120&Signature=vjbyPxybdZaNmGa%2ByT272YEAiv4%3D |
在URL中实现签名,必须至少包含Signature,Expires,OSSAccessKeyId三个参数。Expires这个参数的值是一个UNIX时间(自UTC时间1970年1月1号开始的秒数,详见wiki),用于标识该URL的超时时间。如果OSS接收到这个URL请求的时候晚于签名中包含的Expires参数时,则返回请求超时的错误码。例如:当前时间是1141889060,开发者希望创建一个60秒后自动失效的URL,则可以设置Expires时间为1141889120。
所有的OSS支持的请求和各种Head参数,在URL中进行签名的方法和上节介绍的签名算法基本一样。主要区别如下:
1) 通过URL包含签名时,之前的Date参数换成Expires参数。
2) 不支持同时在URL和Head中包含签名。
3) 如果传入的Signature,Expires,OSSAccessKeyId出现不止一次,以第一次为准。
4) 请求先验证请求时间是否晚于Expires时间,然后再验证签名。
URL中添加签名的python示例代码为:
import base64 import hmac import sha import urllib h = hmac.new("OtxrzxIsfpFjA7SwPzILwy8Bw21TLhquhboDYROV", "GET\n\n\n1141889120\n/oss-example/oss-api.pdf", sha) urllib.quote_plus (base64.encodestring(h.digest()).strip()) |
细节分析:
1) 使用在URL中签名的方式,会将你授权的数据在过期时间以内曝露在互联网上,请预先评估使用风险。
2) PUT和GET请求都支持在URL中签名。
3) 在URL中添加签名时,Signature,Expires,OSSAccessKeyId顺序可以交换,但是如果Signature,Expires,OSSAccessKeyId缺少其中的一个或者多个,返回403 Forbidden。错误码:AccessDenied。
4) 如果访问的当前时间晚于请求中设定的Expires时间,返回403 Forbidden。错误码:AccessDenied。
5) 如果传入请求时间,必须在OSS服务器当前时间之后的15分钟以内。否则返回403 Forbidden。错误码:RequestTimeTooSkewed。
6) 如果Expires时间格式错误,返回403 Forbidden。错误码:AccessDenied。
7) 如果URL中包含参数Signature,Expires,OSSAccessKeyId中的一个或者多个,并且Head中也包含签名消息,返回消息400 Bad Request。错误码:InvalidArgument。
8) 生成签名字符串时,除Date被替换成Expires参数外,仍然包含content-type、content-md5等上节中定义的Header。(请求中虽然仍然有Data这个请求头,但不需要将Data加入签名字符串中)
4.4 Bucket权限控制
OSS提供Bucket级别的权限访问控制,Bucket目前有三种访问权限:public-read-write,public-read和private,它们的含义如下:
n public-read-write:任何人(包括匿名访问)都可以对该bucket中的object进行PUT,Get和Delete操作;所有这些操作产生的费用由该bucket的创建者承担,请慎用该权限。
n public-read:只有该bucket的创建者可以对该bucket内的Object进行写操作(包括Put和Delete Object);任何人(包括匿名访问)可以对该bucket中的object进行读操作(Get Object)。
n private:只有该bucket的创建者可以对该bucket内的Object进行读写操作(包括Put、Delete和Get Object);其他人无法访问该Bucket内的Object。
用户新创建一个新Bucket时,如果不指定Bucket权限,OSS会自动为该Bucket设置private权限。对于一个已经存在的Bucket,只有它的创建者可以通过OSS的 Put Bucket Acl接口修改该Bucket的权限。
相关阅读:
Ø