Windows Azure Storage - 公有云存储签名授权与访问控制
微软公有云存储Windows Azure Storage(WAS)存储提供持久的、可持续的、易扩展的存储空间,并且提供基于Internet的多种访问方式。包含3种类型的存储服务:
- „ 二进制大对象(Binary Large Object(Blob)Storage)服务,主要用于存储文件或二进制数据,用于文件存储分发、流媒体库存储、数据备份等。存储的文件提供对外的HTTP(s)访问地址。
- „ 队列(Queue Storage)服务,用于存储实时消息数据,确保应用程序之间可靠的、持久的消息传递。
- „ 表(Table Storage)服务,用于可被查询的动态结构化数据存储。与SQL Server等传统数据库中数据表不同的是,同一个Table Storage表中的数据实体可以拥有不同的数据结构,如学生成绩信息和客户联系信息,而SQL Server中同一个数据表内不可能同时含有学生成绩信息和客户联系信息。
进一步单独了解WAS提供的编程接口和访问方式,请参考:
如何编程访问Blob存储:https://www.windowsazure.cn/zh-cn/develop/net/how-to-guides/blob-storage-v17/
如何编程访问Table存储:https://www.windowsazure.cn/zh-cn/develop/net/how-to-guides/table-services/
如何编程访问队列存储:https://www.windowsazure.cn/zh-cn/develop/net/how-to-guides/queue-service/
本文将对WAS访问过程中常用的签名授权(Shared Access Signature:SAS)方式及WAS访问控制进行分析。
WAS访问控制
在默认情况下,只有WAS账户所有者能访问该账户下存储的Blob、Table和Queue,如果需要将WAS中存储的部分资源共享给更多的用户,WAS账户所有者除了直接将WAS存储密钥共享之外(一般不推荐),还可以采用以下两种方法。
- „ 设置某个容器的属性为公开,使得匿名用户可以访问该容器下的Blob文件,该方法仅使用于Blob共享。
- „ 使用授权签名的方式,设定WAS账户下的某些Blob、Table和Queue在某段时间内对外部分开放,授权客户端可以在有效时间内使用带有数字签名的请求对目标Blob、Table和Queue执行授权的操作。
授权签名(Shared Access Signature:SAS)是包含有指定权限和有效时间的URI地址,客户端可以在不使用WAS密钥的情况下,使用该URI地址直接操作存储在WAS上的文件、表和队列数据。授权签名在使用过程中主要有两种形式。
- „ 自组织签名(Ad hoc SAS):创建一个独立的授权签名,包括详细的起止时间、授予权限等,适用于Container、Blob、Table和Queue。
- „ 规则授权(SAS with stored access policy):针对Container、Table和Queue创建授权规则,规则中包含一个或多个授权签名。
两种形式的主要区别在于撤销授权上,如果使用自组织签名,含有数字签名的URL一旦被客户端获取,开发者就不能撤销授权,若要使得客户端不能访问WAS上的对应资源,开发者只能等待已存在的授权过期或手动删除原始数据源。但在规则授权方式中,开发者可以在授权到期之前修改规则、删除规则或者重置WAS账户密钥(不推荐),实现终止授权。需要注意的是,若选择删除已有规则来撤销授权,则在原授权到期之前,如需创建新的授权规则,注意一定选用不同的规则名称,否则原有授权规则依然会生效。
下面针对两种不同的授权方式进行编程实践。
1. 使用自组织签名方式进行数据访问:
// using local emulator to practice
var account = CloudStorageAccount.DevelopmentStorageAccount; // or your cloud account
var container = account.CreateCloudBlobClient(). GetContainerReference("testcontainer");
container.CreateIfNotExist();
//create a file in blob storage
var blob = container.GetBlobReference("test.txt");
blob.Properties.ContentType = "text/plain";
blob.UploadText("Hello, World!");
//set the container private
BlobContainerPermissions containerAccess = new BlobContainerPermissions();
containerAccess.PublicAccess = BlobContainerPublicAccessType.Off;
container.SetPermissions(containerAccess);
// create a shared access signature (looks like a query param: ?se=...) to the blob file
var sas = blob.GetSharedAccessSignature(new SharedAccessPolicy()
{
Permissions = SharedAccessPermissions.Read
| SharedAccessPermissions.Write,
SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromSeconds(30)
});
Console.WriteLine("This link should work for the next 30 seconds:");
Console.WriteLine(blob.Uri.AbsoluteUri + sas);
//the above can be done by some server
//the information below can be sent to client for using SAS
string tmp = blob.Uri.AbsoluteUri + sas;
Uri blobEndpoint = account.BlobEndpoint;
以上代码在WAS存储模拟器中创建了一个新的文件,并对该文件进行了授权签名,客户端根据上述产生的授权签名在30秒内可以对该Blob文件进行读写操作。最终得到的授权签名为:
?se=2013-09-16T03%3A57%3A15Z&sr=b&sp=rw&sig=XANHKt2V%2FzWg0URAYnn%2BVzuYJpzTohvn%2Bto0TvwgRog%3D
完整用于访问该Blob文件的授权地址为:
在上述签名和地址中,可以清晰地看到授权的终止时间(se)、授权的对象(sr=blob)、授予的权限(sp= read & write),以及详细的数字签名标识(sig)。
基于上述生成的授权签名,客户端可以在不使用WAS密钥的情况下访问指定的非公开(private)Blob文件。代码如下:
// now just use the SAS to do blob operations
var sasCreds = new StorageCredentialsSharedAccessSignature(sas);
// new client using the same endpoint (including account name)
// but using the SAS as the credentials
var sasBlob = new CloudBlobClient(blobEndpoint, sasCreds)
.GetBlobReference("testcontainer/test.txt");
//write operation here to test
sasBlob.UploadText("Hello again!");
Console.WriteLine("Message after SAS based operation : " + sasBlob. DownloadText());
//read operation here to test
sasBlob.FetchAttributes();
//wait for some time to get SAS expired, then try to do write operation
System.Threading.Thread.Sleep(30000);
try
{
sasBlob.UploadText("Hello again + 3!");
}
catch (Exception exx)
{
Console.WriteLine(exx.Message);
}
客户端使用授权签名在规定的时间内(30秒)可以对非公开的(private)Blob文件进行读写操作,但30秒过后,客户端的后续操作会失败,并得到以下错误信息:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
这是由于签名已过期,基于签名的操作无法通过WAS身份验证。
2. 使用规则授权方式访问WAS资源:
// using local emulator to practice
var account = CloudStorageAccount.DevelopmentStorageAccount; // or your cloud account
var container = account.CreateCloudBlobClient(). GetContainerReference("testcontainer");
container.CreateIfNotExist();
//create a file in blob storage
var blob = container.GetBlobReference("test.txt");
blob.Properties.ContentType = "text/plain";
blob.UploadText("Hello, World!");
//set the container private
BlobContainerPermissions containerAccess = new BlobContainerPermissions();
containerAccess.PublicAccess = BlobContainerPublicAccessType.Off;
//create one share access policy
SharedAccessPolicy sap0 = new SharedAccessPolicy()
{
Permissions = SharedAccessPermissions.Read
| SharedAccessPermissions.Write,
SharedAccessExpiryTime = DateTime.UtcNow + TimeSpan.FromSeconds(30)
};
//add the policy to the target container
containerAccess.SharedAccessPolicies.Add("mypolicy", sap0);
container.SetPermissions(containerAccess);
// create a shared access signature (looks like a query param: ?se=...)
var sas0 = container.GetSharedAccessSignature(new SharedAccessPolicy(), "mypolicy");
Console.WriteLine("This link should work for the next 30 seconds:");
Console.WriteLine(blob.Uri.AbsoluteUri + sas0);
string tmp = blob.Uri.AbsoluteUri + sas0;
创建的授权签名为:
签名中指定了规则应用对象(sr=container)、规则名称(si=mypolicy)和数字签名标识(sig)。客户端可以在获取上述签名信息后,在不使用WAS密钥的情况下直接访问指定Container下的数据。代码如下:
// now just use the SAS to do blob operations
var sasCreds = new StorageCredentialsSharedAccessSignature(sas0);
// new client using the same endpoint (including account name)
// but using the SAS as the credentials
var sasBlob = new CloudBlobClient(account.BlobEndpoint, sasCreds)
.GetBlobReference("testcontainer/test.txt");
sasBlob.UploadText("Hello again!");
Console.WriteLine("Message after SAS based operation : " + sasBlob. DownloadText());
System.Threading.Thread.Sleep(30000);
try
{
sasBlob.UploadText("Hello again + 3!");
}
catch (Exception exx)
{
Console.WriteLine(exx.Message);
}
与自组织签名一样,一旦规则授权中的数字签名过期,客户端的后续操作就不会成功,并得到以下身份验证错误信息:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
对于已授权的签名,若开发者需要及时撤销授权,则可以通过管理Container对应的SharedAccessPolicies来实现。代码如下:
// now just use the SAS to do blob operations
var sasCreds = new StorageCredentialsSharedAccessSignature(sas0);
// new client using the same endpoint (including account name)
// but using the SAS as the credentials
var sasBlob = new CloudBlobClient(account.BlobEndpoint, sasCreds)
.GetBlobReference("testcontainer/test.txt");
sasBlob.UploadText("Hello again!");
Console.WriteLine("Message after SAS based operation : " + sasBlob. DownloadText());
//delete the policy when the signature is still valid
containerAccess.SharedAccessPolicies.Remove("mypolicy");
container.SetPermissions(containerAccess);
//operation will fail since SAS was revoked
try
{
sasBlob.UploadText("Hello again + 2!");
}
catch (Exception exx)
{
Console.WriteLine(exx.Message);
}
调试运行,可以发现,撤销已存在的授权规则后,客户端将不能访问指定的WAS资源,并获得错误信息:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
上述介绍中均以Blob为例,实现基于授权签名的WAS访问和授权控制,同样的方法也适用于Table Storage和Queue Storage。
将本地存储模拟器切换为Azure存储
以上的SAS测试是在本地模拟器上测试的,要将测试对象切换为真正的Azure存储,只需将
var account = CloudStorageAccount.DevelopmentStorageAccount; // or your cloud account
改为开发者在Azure Cloud中已有的Storage帐号即可。
如开发者使用China Azure时,WAS连接字符串类似以下样例:
CloudStorageAccount storageAccount = CloudStorageAccount.Parse("BlobEndpoint=https://portalvhds88zyhq6kndn2p.blob.core.chinacloudapi.cn/;AccountName=portalvhds88zyhq6kndn2p;AccountKey=tbLt4dmW0vJFFYobuBvpN2RB6On63XjwKDkoxD/v7Hg1vkeS3y3FJ+BK0MT6Q1MYXCWbKOJSM8+9mUrHUzrLDg==");
如开发者使用Global Windows Azure,WAS连接字符串类似以下样例:
CloudStorageAccount.Parse.("DefaultEndpointsProtocol=https;AccountName=mytest3939393;AccountKey=sZjfJiETVsXC****&&$$$m4yfjdajf*****%%U3fvxVHqGmJhIcaVaX3g==");
结论
开发者在使用WAS存储时,一定要尽可能的保护原始WAS密钥信息(连接字符串),对于Client/Server架构的应用,尽可能采用上述授权签名(SAS)的方式动态的安全授权。