System.Net.Caching.HttpRequestCachePolicy与CloudBlob.FetchAttributes之间的冲突

时间:2012-11-13 22:05:14

标签: .net c#-4.0 azure azure-storage

我似乎得到了403:

  

HTTP / 1.1 403服务器无法验证请求。确保正确形成Authorization标头的值,包括签名。

当我在app.config中设置HttpWebRequest.DefaultCachingPolicy时,会发生这种情况,如下所示:

<system.net>
 <requestCaching defaultPolicyLevel="Default" isPrivateCache="false">
  <defaultHttpCachePolicy policyLevel="Default"/>
 </requestCaching>
</system.net>

我这样做是因为我有遗留的代码,我无法控制它调用我提供的blob存储的API,就好像它是一个文件系统(每个文件最多58个相同的调用)。显然这并不理想。使用默认的HTTP样式缓存是我想要的一种行为,因为它会导致我的应用程序仅在修改文件时下载该文件。

问题似乎发生在每个其他请求中(例如,当请求被缓存并且服务器正在检查服务器内容是否已更改时,它似乎正在发生。)

失败请求与成功请求之间的唯一区别似乎是包含:

If-None-Match: "<a blob etag>"
If-Modified-Since: <a date>

我已经在github for 1.7.1上查看了.net API(我正在使用)的代码,并假设它没有从SDK 1.6(我目前使用的)改变,它应该工作细

非常感谢任何帮助

更新: 我写了一些repro代码来帮助:

使用:.NET 4.0,Windows Azure SDK 1.6

using System;
using System.Net;
using System.Net.Cache;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace AzureStorageProb
{
    class Program
    {
        static void Main(string[] args)
        {
            const string accountKey = "<azure storage account key>";
            const string account = "<azure storage account name>";
            const string testBlob = "<blob path to test file>";
            var cloudStorageAccount = 
               new CloudStorageAccount(
                  new StorageCredentialsAccountAndKey(account, accountKey),
                  useHttps: true);
            var cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
            HttpWebRequest.DefaultCachePolicy = 
               new HttpRequestCachePolicy(HttpRequestCacheLevel.Default);

            try
            {
                var blob = cloudBlobClient.GetBlobReference(testBlob);
                blob.FetchAttributes();
                blob.DownloadByteArray();
                Console.WriteLine("First attempt worked!");
            }
            catch (StorageClientException ex)
            {
                Console.WriteLine(ex);
            }

            try
            {
                var blob = cloudBlobClient.GetBlobReference(testBlob);
                blob.FetchAttributes();
                blob.DownloadByteArray();
                Console.WriteLine("Second attempt worked!");
            }
            catch (StorageClientException ex)
            {
                Console.WriteLine(ex);
            }
            Console.ReadKey();
        }
    }
}

2 个答案:

答案 0 :(得分:0)

事实证明,罪魁祸首是BlobRequest.SignRequest(request, creds);快速查看文档表明If-None-MatchIf-Modified-Since都用作调用添加的Authentication头的计算的一部分。因为在WINInet计算Authentication头之后添加了这些头(假设)。无论哪种方式,最终结果都是Authentication头提供的校验和现在无效。从而导致403 Forbidden

解决方法:

  • 在本地手动缓存项目
  • 修复呼叫代码(可能是最佳长期解决方案)
  • 非常仔细地使用其余界面(由于同样的原因,实验表明这是危险的,在某些情况下使用'Shared Key Lite'解决这个问题)
  • 使用常规WebRequest获取文件(这意味着您必须通过共享密钥或其他方式将它们暴露给互联网)

我不会说Azure Blob存储已损坏......但我会说它不是真正的RESTful,因为它无法正确遵守HTTP语义。

答案 1 :(得分:0)

实现失败的原因是计算的签名包括请求日期以及条件标头(如果匹配,如果没有匹配,如果没有匹配,等等),以防止重放或人员中间攻击。因此,当发送高速缓存的请求时,认证失败。 目前似乎没有办法挂钩到默认HTTP缓存并更改标头以更新此签名,因此SharedKey和SharedKeyLite身份验证方案将不适用于此方案。 但是,您可以使用SharedAccessSignatures(http://msdn.microsoft.com/en-us/library/windowsazure/ee395415.aspx)在给定URI上预先验证一段时间。这将不再需要每个请求唯一的签名。

实施例

SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy()
{
     Permissions = SharedAccessBlobPermissions.Read,

     // Add delta to account for clock skew
     SharedAccessStartTime = DateTime.Now.AddMinutes(-5),
     SharedAccessExpiryTime = DateTime.Now.AddMinutes(15)
};

HttpWebRequest.DefaultCachePolicy = new HttpRequestCachePolicy(HttpCacheAgeControl.MaxAge, TimeSpan.FromSeconds(10));
CloudBlockBlob sasdBlob = new CloudBlockBlob(new Uri(rootBlob.Uri.ToString() + rootBlob.GetSharedAccessSignature(policy)));

OperationContext cacheCtx = new OperationContext();
cacheCtx.ResponseReceived += (o, a) => Console.WriteLine("{0} : {1}", a.RequestInformation.HttpStatusCode, a.Response.IsFromCache);

for (int m = 0; m < 100; m++)
{
     sasdBlob.DownloadToStream(Stream.Null, null, null, cacheCtx);
     Thread.Sleep(1000);

     if (m == 10)
     {
          // invalidate data updating properties
          rootBlob.Metadata.Add("hello", "cache");
          rootBlob.SetMetadata();
     }
}

这将输出:

200:错误

200:是的

200:是的

200:是的

200:是的

200:是的

200:是的

200:是的

200:是的

200:是的

200:是的

200:错误

200:是的

...

我们将在没有这些变通方法的情况下寻找更好的方法来公开此功能。

阅读本文!

每当您使用SharedAccessSignatures时,您实际上是在URI内部发布密钥。因此,应遵循一些安全最佳实践。

  1. 如果您在DC外部或通过可能受监控的链接运行,请使用HTTPS以防止恶意行为者滥用签名。
  2. 上面的示例计算了一个与Container ACL无关的简单策略,因此撤销它的唯一方法是旋转存储密钥。请尽可能将SAS签名与容器策略相关联。在这种情况下,如果SAS Uri被泄露/滥用,您可以通过简单地删除相关容器上的策略来撤销它。有关如何执行此操作的详细信息,请参阅:http://msdn.microsoft.com/en-us/library/windowsazure/jj721951.aspx
  3. 所有SAS流量都计入给定帐户SLA和Billing,因此仅与受信任客户端共享SAS URI。
  4. 我希望这有帮助,如果您有任何其他问题,请告诉我,

    /乔