访问控制

用户签名验证(Authentication)

NOS 通过使用访问凭证 ID(AccessKey)和访问凭证密钥(SecretKey)非对称加密的方法来验证某 个请求的发送者身份。AccessKey 用于标识用户,SecretKey 是用户用于加密签名字符串和 NOS 用来验证签名字符串的密钥,其中 SecretKey 必须保密,只有用户和 NOS 知道。每个访问凭证 对都有 active/inactive 两种状态:

* active 表明用户可以用此 ID 对签名验证请求

* inactive 表明用户暂停此 ID 对签名验证的功能

一个用户可以同时拥有多个 active 或者 inactive 的 ID 对。用户可以登录 API KEY 管理页面查看 管理访问凭证。当用户想以个人身份向 NOS 发送请求时,需要首先将发送的请求按照 NOS 指定 的格式生成签名字符串;然后使用 SecretKey 对签名字符串进行加密产生验证码。NOS 收到请 求以后,会通过 AccessKey 找到对应的 SecretKey,以同样的方法提取签名字符串和验证码, 如果计算出来的验证码和提供的一样即认为该请求是有效的;否则,NOS 将拒绝处理这次请求 ,并返回相关的错误码。例如若 AccessKey 格式有误,返回错误码:InvalidAccessKeyId。

在 Head 中包含签名

用户可以在 HTTP 请求头中增加 Authorization(授权)来包含签名信息,表明这个请求已被授 权。如果用户的请求中没有 Authentication 字段,则认为是匿名访问。

验证码计算

Authorization = "NOS " + AccessKey + ":" + Signature
Signature =	Base64(HMAC-SHA256(SecretKey,
              HTTP-Verb + "\n"  
              + Content-MD5 + "\n"  
              + Content-Type + "\n"  
              + Date + "\n"  
              + CanonicalizedHeaders 
              + CanonicalizedResource))

说明:

* HTTP-Verb 表示 HTTP 请求类型,如:PUT,GET,DELETE 等

* Content-MD5 表示内容数据的 MD5 值,某些 API 该字段非必须

* Content-Type 表示内容的类型,某些 API 该字段非必须

* Date 表示此次操作的时间,格式必须符合 RFC1123 的日期格式,示例:Wed, 01 Mar 2009 12:00:00 GMT

* CanonicalizedHeaders 表示请求中其他重要的 HTTP 头。

* CanonicalizedResource 表示用户想要访问的 NOS 资源。

其中,Date 和 CanonicalizedResource 不能为空,其余字段如为空,用空字符串""代 替;如果请求中的 Date 时间和 NOS 服务器的时间差正负 15 分钟以上,NOS 服务器将拒绝该服务 ,并返回错误码:AccessDenied。

构建 CanonicalizedHeaders 的方法

所有以 x-nos- 为前缀的 HTTP Header 被称为 CanonicalizedHeaders。它的构建方法如下:

1. 将所有以 x-nos- 为前缀的 HTTP 请求头的 key 转换成小写字母。如 "x-nos-meta-Name: Easyread" 转换成 "x-nos-meta-name: Easyread"。

2. 如果有相同名字的请求头,则根据标准 RFC 2616 进行合并(两个值之间只用逗号分隔)。如有两个名为 "x-nos-meta-name" 的请求头,对应的值分别为 "photo" 和 "Easyread",则合并后为:"x-nos-meta-name:photo,Easyread" 。注意:合并之后的顺序和请求头中出现的顺序一致。

3. 将上一步得到的所有 HTTP 请求头按照字典序进行升序排列。

4. 删除请求头和内容之间分隔符两端出现的任何空格。如 "x-nos-meta-name  : photo,Easyread" 转换成:"x-nos-meta-name:photo,Easyread"。

5. 将所有的头和内容用 \n 分隔符分隔拼成最后的 CanonicalizedHeader。

构建 CanonicalizedResource 的方法

用户发送请求中想访问的 NOS 目标资源被称为 CanonicalizedResource 。它的构建方法如下:

* 将 CanonicalizedResource 置成空字符串""

* 放入要访问的 NOS 资源:对于 ListBucket 操作,资源为/;对于桶操作,资源为 /BucketName/;对于对象操作,资源为 /BucketName/ObjectName(含有路径前缀:/BucketName/路径前缀%2FObjectName,例如 bucketname 为 file201503,资源路径为/domain/domain.txt,则CanonicalizedResource 为/file201503/domain%2Fdomain.txt);

* 如果请求包含子资源( sub-resource ),那么将所有子资源的 Key 按照字典序排列,并以”&”为分隔符生成子资源字符串。在 CanonicalizedResource 字符串尾添加”?”和子资源字符串。示例:/BucketName/ObjectName?partNumber=PartNumber&uploadId=UploadId

Attention:

sub-resource,指的是 NOS 里面某些特殊的请求参数。NOS 当前支持的子资源包括:”acl”, “location”, “uploadId”, “uploads”, “partNumber”, “delete”等

     

在计算签名头的时候请遵循如下规则

1、用来签名的字符串为 UTF-8 格式,URL 中的 resource 和 queryString 应该经过 URL encode。

2、签名的方法用 RFC 2104 中定义的 HMAC-SHA256 方法,其中 Key 为 Secretkey。

3、Content-Type 和 Content-MD5 在请求中不是必须的,但是如果请求需要签名验证,空值的话以回车符代替。

4、在所有非 HTTP 标准定义的 header 中,以 x-nos- 开头的 header,都需要加入签名字符串。

5、以 x-nos- 开头的 header 在签名验证前需要符合以下规范:

* header 的名字需要变成小写。

* header 按字典序自小到大排序。

* 分割 header name 和 value 的冒号前后不能有空格。

* 每个 Header 之后都有一个回车,如果没有 Header,CanonicalizedHeaders 就设置为空。

注意事项

1. 如果传入的 AccessKey 不存在或 inactive,返回 403 Forbidden。错误码:InvalidAccessKeyId。

2. 若用户请求头中 Authorization 值的格式不对,返回 403 Forbidden。错误码:InvalidAccessKeyId。

3. 如果签名验证的时候,请求头中没有传入Date或者格式不正确,返回 403 Forbidden 错误。错误码: AccessDenied。

4. 传入请求的时间必须在 NOS 服务器当前时间之后的 15 分钟以内,否则返回 403 Forbidden。错误码:RequestTimeTooSkewed。

5. 如果 AccessKey 是 active 的,但 NOS 判断用户的请求发生签名错误,则返回 403 Forbidden,错误码 AccessDenied。

6. 计算签名的逻辑,可参看各类 SDK 源码。

Note:

为方便获取 openapi 中使用的 signature 我们提提供shell脚本实例 javascript 版本 demo C++ 版本 demo

1.shell脚本通过PUT Object来示范如何用Linux命令构造简单的NOS请求,更复杂的请求建议使用SDK。
2. javascript 版本 demo 仅需修改文件 index.html,C++ 版本 demo 仅需修改 auth.cpp 文件的 main 函数。
3. demo 中仅需修改 accessId、secretKey、date、resource 值为自己的值,其中 date 为未来 15 分钟内的时间,且为 GMT 时间(比东八区晚8小时),openapi 中的 date 值需要与此值一致, resource 为用户自己的桶、对象路径,openapi 中当需要列出全部桶时此值为"/"。
4. 修改完 javascript 版本 demo 后直接浏览器运行 index.html 文件即可获取到 signature 值。
5. 修改完 C++ 版本 demo 后需要执行命令 yum install libcurl-dev libcurl-devel -y 安装依赖库,执行 g++ -o auth auth.cpp -lssl -lcrypto 完成编译,运行生成的文件 auth 即可获取到 signature 值。

在 URL 中包含签名

除了使用 Authorization Header Http 头部之外,私有桶的访问外链用户还可以在 URL 中加入 签名信息,这样用户就可以把私有桶资源,以签名 HTTP URL 方式转给第三方实现授权访问。

URL中包含签名示例

无路径前缀:http://myBucket.nos-eastchina1.126.net/myObject?NOSAccessKeyId=20be823cfcd8459f944e9c189224e7c1&Expires=1499758765&Signature=HrQvKIX5cs1SR3242qUajglrUzQDv7vyqPxN4gepDt4%3D
有路径前缀:http://myBucket.nos-eastchina1.126.net/路径前缀%2FmyObject?NOSAccessKeyId=20be823cfcd8459f944e9c189224e7c1&Expires=1499758765&Signature=HrQvKIX5cs1SR3242qUajglrUzQDv7vyqPxN4gepDt4%3D

转换规则示例:/music/test.mp3  ——>   %2Fmusic%2Ftest.mp3

Attention

注意: 一些编程语言的方法可能不能将'/' 进行转码,比如 python3 的parse.quote()方法,请确保签名和 URL 编码中'/'已经被转码,否则可能会出现 403 错误。例如,python3 建议使用 parse.quote_plus 方法。

在 URL 中实现签名,必须至少包含 Signature,Expires,NOSAccessKeyId 三个参数。Expires 这个参数的值是一个 UNIX 时间(自 UTC 时间 1970 年 1 月 1 号开始的秒数), 用于标识该 URL 的超时时间。如果 NOS 接收到这个 URL 请求的时候晚于签名中包含的 Expires 参 数时,则返回请求超时的错误码。例如:当前时间是 114189060,开发者希望创建一个 60 秒后 自动失效的 URL,则可以设置 Expires 时间为 1141889120。

所有 NOS 支持的请求和各种 Header 参数,在 URL 中进行签名的方法和上节介绍的签名算法基本一样。主要区别如下:

* 通过 URL 包含签名时,之前的 Date 参数换成 Expires 参数。

* 对签名串做 URL-Encode 编码。

* 对URL而言,Content-MD5、Content-Type等都是空串("")。

  Signature = URL-Encode (Base64(HMAC-SHA256(SecretKey,
              HTTP-Verb + "\n"
              + Content-MD5 + "\n"
              + Content-Type + "\n"
              + Expires + "\n"
              + CanonicalizedHeaders
              + CanonicalizedResource))

Attention

不支持同时在URL和Head中包含签名。

如果传入的Signature,Expires,NOSAccessKeyId出现不止一次,以第一次为准。

请求先验证请求时间是否晚于Expires时间,然后再验证签名。

URL 过期后需要重新生成。

为方便获取签名,我们提供 javascript 版和 python 版的demo示例:

1.点击下载JavaScript 示例 demo ,点击获取 python 示例

2.demo仅需修改index.html文件中以下四个参数:

accessId的值为你的Access Key;

secretKey的值为你的Access Secret;

expires的值为超时时间点,需要晚于当前时间点;

resource的值:对于桶操作,值为 /BucketName/;对于对象操作,值为 /BucketName/ObjectName(含有路径前缀:/BucketName/路径前缀%2FObjectName)

注意事项

1.使用在 URL 中签名的方式,会将你授权的数据在过期时间以内曝露在互联网上,请预先评估使用风险。

2.URL 签名只支持 GET 请求下载 Object。

3. 在 URL 中添加签名时,Signature,Expires,NOSAccessKeyId 顺序可以交换,但是如果 Signature,Expires,NOSAccessKeyId 缺少其中的一个或者多个,返回 403 Forbidden。错误码:AccessDenied。

4.如果访问的当前时间晚于请求中设定的 Expires 时间,返回 403 Forbidden。错误码:AccessDenied。

5. 如果传入请求时间,必须在 NOS 服务器当前时间之后的 15 分钟以内。否则返回 403 Forbidden。错误码:RequestTimeTooSkewed。

6. 如果 Expires 时间格式错误,返回 403 Forbidden。错误码:AccessDenied。

7. 如果 URL 中包含参数 Signature,Expires,NOSAccessKeyId 中的一个或者多个,并且 Head 中也包含签名消息,返回消息 400 Bad Request。错误码:InvalidArgument。

8. 生成 URL 中的签名字符串时,除 Date 被替换成 Expires 参数外,仍然包含 content-type、content-md5、CanonicalizedHeaders、CanonicalizedResource 等上节中定义的 Header。(请求中虽然仍然有 Date 这个请求头,但不需要将 Date 加入签名字符串中)

Bucket权限控制

NOS 提供 Bucket 级别的权限访问控制,Bucket 目前有两种访问权限:Public-read 和 Private,它们的含义如下:

* Public-read:只有该 Bucket 的创建者可以对该 Bucket 内的 Object 进行写操作(包括 Put 和 Delete Object);任何人(包括匿名访问)可以对该 Bucket 中的 Object 进行读操作(Get Object)。

* Private:只有该 Bucket 的创建者可以对该 Bucket 内的 Object 进行写操作(包括 Put 、 Delete );其他人可以访问该 Bucket 内的 Object,不能进行对象的写操作。

用户新创建一个 Bucket 时,如果不指定 Bucket 权限,NOS 会自动为该 Bucket 设置 Private 权限。对于一个已经存在的 Bucket,只有它的创建者可以修改该 Bucket 的权限。

Object外链地址的构成规则

如果一个 Bucket 设置成 Public-read 权限,意味着你允许其他用户来访问属于你的 Object。你的 Object 的外链地址构成规则如下:

 http://<BucketName>.endpoint/<ObjectName>

例如:在一个杭州分区 Bucket(名字为photo)中,放了名为一个 ”image/test.jpg” 的 Object,那么这个 Object 的内网 URL 为: 

 http://photo.nos-eastchina1-i.netease.com/image/test.jpg

* photo 是 Bucket

* nos-eastchina1-i.netease.com 为杭州分区的内网访问域名

* image/test.jpg 是 Object

用户可以将该 URL 链接放入 HTML 中:

 <img src=”http://photo.nos-eastchina1-i.netease.com/image/test.jpg” />