云安全

Windows Azure 中的加密服务和数据安全

Jonathan Wiggs

Windows Azure 平台的许多早期采用者仍对平台安全及其加密支持存在大量疑问。在此,我将介绍 Windows Azure 平台内加密和相关安全的一些基本概念。详细阐述本主题可能需要很大的篇幅,因此我只打算说明并重温一下 Windows Azure 中的某些加密服务和提供程序。任何向 Windows Azure 的过渡也会存在一些安全隐患。

对于任何新平台或服务交付方法,您都会面临新的挑战。另外还要提醒您,一些典型问题仍然存在,甚至您过去使用的一些相同的解决方案仍将有效。任何应用程序工程师或设计人员都应仔细思考本主题,因为它与您可能存储及需要保留的数据类型有关。将此方法与系统化方法相结合,会为您和您的客户提供优质服务。

因此,为什么我会认为开发人员社区中需要此信息呢?在过去的几个月中,我发现社区站点中有关 Azure 基本安全性的文章越来越多。Microsoft 建议将加密作为保护 Azure 项目的应用层数据的一部分。但是,构建 Windows Azure 平台的产品设计人员和开发人员需要正确理解加密和 .NET 安全模型。

我发现这么一件事:特定于加密服务和密钥存储的文章的百分比不断增加。对于 Windows Azure Storage 服务来说,更是如此。这勾起了我的好奇心,我发现这是一个值得深入讨论的话题。

在撰写本文的过程中,我将大量使用加密服务提供程序 (CSP),在该程序中可实施系统编程接口中的加密标准、算法和函数。出于本文的目的,我将使用由 Rijndael 加密类提供的对称加密算法。

加密基础知识

Windows Azure SDK 扩展了核心 .NET 库,允许开发人员集成并使用由 Windows Azure 提供的服务。对 CSP 的访问并不仅限于在 Windows Azure 项目和服务中进行。这意味着您的许多与加密和解密数据有关的开发将与您习惯使用的程序集保持一致。但是,基本体系结构有一些更改,即加密数据的时间或位置及保存密钥的位置和方式的问题。本文稍后将讨论一些关键数据和机密数据持久性。

您还能够访问 Windows Azure 中加密哈希功能的完整阵列,例如 MD5 和 SHA。以上内容对增强任何系统的安全性(如检测重复的数据、哈希表索引、消息签名和密码验证等)至关重要。

一致建议始终不创建您自己的加密算法或使用专有加密算法。.NET CSP 中提供的算法已得到证实并经过测试,已公开很多年供备份。使用 XOR 创建您自己的加密过程并不相同,并且不提供相同级别的数据安全性。

第二个建议是使用 RNGCryptoServiceProvider 类生成随机数字。这可以确保您的应用程序生成的随机数字始终具有极高级别的平均信息量,从而在模式上难以猜测。

以下代码实现了单个静态成员,该成员将返回随机的 32 位 int 值且满足加密学角度上的安全需要。通过使用 Cryptography 命名空间中提供的 RNGCryptoServiceProvider 中的字节生成器可以实现这一点:

public static int GenerateRandomNumber() {
  byte[] GeneratedBytes = new byte[4];
  RNGCryptoServiceProvider CSP = new RNGCryptoServiceProvider();
  CSP.GetBytes(GeneratedBytes);
  return BitConverter.ToInt32(GeneratedBytes, 0);
}

图 1 显示了在 Windows Azure 平台内部使用 CSP 的简单示例。公开了三个公共成员,用于在任何 Windows Azure 应用程序中使用。第一个成员接受二进制密钥和初始化向量 (IV) 及未加密数据的二进制缓冲区并返回其加密的等效方法。第二个成员通过解密数据执行相反的操作。第三个成员返回为该数据计算出的哈希值。在此请注意,我使用 Rijndael CSP 对提供程序进行托管访问。我还在二进制缓冲区中存储数据和密钥并在完成后进行改写。稍后将在讨论不变性时介绍本主题。

图 1 简单加密

public static byte[] SampleEncrypt(byte[] dataBuffer, 
  byte[] Key, byte[] IV) {

  MemoryStream InMemory = new MemoryStream();
  Rijndael SymetricAlgorithm = Rijndael.Create();
  SymetricAlgorithm.Key = Key;
  SymetricAlgorithm.IV = IV;
  CryptoStream EncryptionStream = new CryptoStream(InMemory,
    SymetricAlgorithm.CreateEncryptor(), CryptoStreamMode.Write);
  EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
  EncryptionStream.Close();
  byte[] ReturnBuffer = InMemory.ToArray();
  return ReturnBuffer;
}

这是加密数据并以字节数组形式返回加密结果的最简单示例。这不是可以在未进行所有适当的安全性分析的情况下在安全环境中使用的代码,只是一个示例。 

图 2 中的示例几乎与图 1 中的示例具有相同的结构。在本例中,我基于相同的密钥和 IV 解密数据,仅将加密的字节缓冲区作为参数使用。这里唯一真正的差别就是当创建加密流时,我指定创建对称解密器而不是先前创建的加密器。

图 2 简单解密

public static byte[] SampleDecrypt(byte[] dataBuffer, 
  byte[] Key, byte[] IV) {

  MemoryStream InMemory = new MemoryStream();
  Rijndael SymetricAlgorithm = Rijndael.Create();
  SymetricAlgorithm.Key = Key;
  SymetricAlgorithm.IV = IV;
  CryptoStream EncryptionStream = new CryptoStream(InMemory,
    SymetricAlgorithm.CreateDecryptor(), CryptoStreamMode.Write);
  EncryptionStream.Write(dataBuffer, 0, dataBuffer.Length);
  EncryptionStream.Close();
  byte[] ReturnBuffer = InMemory.ToArray();
  return ReturnBuffer;
}

密钥存储和持久性

同应用程序或企业层次的任何加密策略一样,加密和解密基础结构远未成功。实际问题在于密钥存储和密钥持久性。 由加密数据提供的数据安全仅取决于使用的密钥,此问题比人们最初想到的要难得多。我所介绍的系统已经在各处存储了加密密钥,从直接存储在源代码中到存储在巧妙命名的文本文件,以及存储在难以找到的目录中的平面文件中。

考虑在云环境中存储和保留密钥的位置时,密钥持久性的重要问题浮现出来。某些人表示担心通过在云中保存密钥,您会受到来自云本身的安全威胁。也就是说,如果某人对您的数据具有物理访问权限,默认情况下可能不会加密磁盘中存储的数据(对于 Windows Azure 也是如此)。考虑到 SQL Azure 尚未支持加密,这成为在规划和设计解决方案时考虑的安全决策。与任何安全性实现一样,必须对风险进行度量、权衡和缓解。

但这并不意味着云平台(通常情况下)和 Windows Azure(特别情况下)在本质上不安全。可能为您提供的其他选项呢?

现在需要注意的是,应用程序始终不应将由 Windows Azure 提供的任何密钥用作加密数据的密钥。示例如下:由 Windows Azure 提供用于存储服务的密钥。这些密钥被配置为可以轻松旋转,以提高安全性或防止某种原因遭到破坏。换句话说,以后可能不存在密钥,并且可能分布极其广泛。

在 Windows Azure Storage 服务中存储您自己的密钥库是一个保存某些机密信息的好方法,因为您可以使用在多租户环境中保证安全的数据并使用您自己的存储密钥保证安全。这不同于将存储密钥用作您的加密密钥。实际上,如同任何其他存储文件一样,可使用存储服务密钥访问密钥库。实现非常简单。例如,假设您想将自己的密钥库作为简单文本文件来实现,以保留某些机密信息。相对于队列或表存储服务而言,最好作为数据存储在 blob 服务 API 中。存储服务的 blob 区域是二进制音频和图像甚至文本文件等数据的最佳位置。服务的队列部分着重介绍了保护不会长期保留的小型数据对象的消息传送的安全。表存储系统非常适合于需要在特定部分保留和访问的结构化数据和信息,这与数据库中的关系数据完全相同。

首先,将密钥保存在 CSP 密钥容器中。这是用于存储公钥的最佳选项,不具有对该服务器的物理访问权限,很难对该密钥进行检索。对于 Windows Azure(其中,应用程序和数据的位置很抽象),这将使查找和检索以这种方式存储的公钥极其困难。创建密钥存储容器非常简单;下面是使用创建密钥的 RSA 提供程序的一个示例。如果密钥容器已存在,会自动将其密钥加载到该提供程序:

CspParameters CspParam = new CspParameters();
CspParam.KeyContainerName = "SampleContainerName";
RSACryptoServiceProvider RSAProvider = new 
  RSACryptoServiceProvider(CspParam);

此外,还提供了您可以根据需要考虑的其他选项。例如,您可以使用特定标记为创建该容器的用户保证密钥的安全。可使用 CspParameters 标记成员完成此操作:

CspParam.Flags = CspProviderFlags.UseUserProtectedKey;

现在,使用 Windows Azure 存储密钥创建一个 blob API 请求。该请求本身要求使用签名字符串和正确的请求标题。正确的标题格式为:

Authorization="[SharedKey|SharedKeyLite] <AccountName>:<Signature>"

在本示例中,我希望最大程度地提高保留的机密数据的 安全性,因此我将使用 SharedKey 授权方法。此标题的签名部分是一个基于哈希的身份验证代码,由 SHA256 算法和您的存储密钥针对签名中的数据生成。然后,此哈希被编码为 base64 字符串。示例签名可能如下所示:

"PUT\n\ntext/plain; charset=UTF-8\n\nx-ms-Date:Fri, 12 Sep 2009 22:33:41 GMT\nx-ms-meta-m1:v1\nx-ms-meta-m2:v2\n/exampleaccount/storageclientcontainer/keys.txt"

如前所述,然后会生成 base64 编码哈希并将其用作标题中的签名。然后,只有以下人员才能访问该密钥文件:其应用程序在 Windows Azure 云中的应用程序空间中运行并有权访问您的存储密钥。因此,借助密钥持久性,您既可以管理 Windows Azure 框架外部的密钥,还可以管理云本身内部的密钥。

密钥和安全威胁

至少值得简要介绍的一项是密钥安全性。这与如何保留和存储密钥略有不同。密钥本身实质上是具有极高级别的平均信息量(也就是极高级别的随机性)的字符串。实际上,这可能会导致查找系统中的密钥的常见的攻击进程。例如,如果转储硬盘上的内存或数据区域,则具有极高平均信息量的区域是您开始查找密钥的最佳位置。

除了基于应用程序的需要选择很好的安全实践和保护数据的安全之外,您还有其他办法保护自身的安全吗?首先,始终假定您解密、加密和保护数据安全所使用的过程为所有攻击者所熟知。 记住这一点后,确保定期交替使用您的密钥并保证其安全。仅为必须利用这些密钥的人员提供密钥,并尽量减少暴露不受控制的密钥的机会。

最后,花时间将您的安全和不安全数据流绘制成图表。请看一下您的数据流往的位置和流去途径、存储机密的位置以及特别是数据跨越边界(例如,公共网络和专用网络)的位置。这将使您很好地了解公开数据的位置并允许您通过直接迁移风险的计划来应对这些风险。

曾有人问过我一个相关问题:Windows Azure 是否支持 SSL。简短的回答是“是”。Windows Azure 对于基于 Web 的服务和不支持 SSl 的应用程序来说可能不是一个功能非常强大的云平台。

使用 SQL Azure 进行加密

SQL Server 2008 的发布引入了一个新功能:透明数据加密 (TDE)。SQL Server 第一次可以仅凭比 SQL Server 2005 中提供的有限加密稍多一点的工作完全加密其数据。但是,SQL Azure 存储的初始版本尚不支持数据库级加密,尽管这是未来版本中考虑的功能。应注意的是,SQL Azure 仅可通过端口 1433 和 TCP 连接使用;目前还无法对其他端口公开。

尽管此功能尚未集成到 Windows Azure 中,但是开发人员或设计人员应牢记 SQL Azure 的几个安全功能。首先,SQL Azure 支持使用表格格式数据流 (TDS)。这表示,大多数情况下,您可以像以前一样连接数据库并与该数据库进行交互。绝对值得考虑利用 ADO.NET 加密和受信任的服务器证书,特别是当从云外部访问您的 SQL Azure 数据库时。

正确组合中的连接属性 Encrypt=True 和 TrustServerCertificate = False 将确保数据传输的安全性并帮助防止中间人攻击。这也是连接到 SQL Azure 的要求 — 您无法连接到 SQL Azure,除非已启用连接级别的加密。

您应当熟悉的 SQL Azure 的第二个安全功能是 SQL Azure 防火墙。这将是使用过本地软件防火墙甚至是 SQL Server 安全外围应用工具集的人比较熟悉的工具。它使您能够允许或阻止从各种源一直到特定 IP 地址或范围的连接。可通过 SQL Azure 门户管理 SQL Azure 防火墙或使用提供的存储进程(如 sp_set_firewall_rule 和 sp_delete_firewall_rule)直接在主数据库中对其进行管理。

与任何 SQL Server 实现一样,还必须严格控制用户帐户管理。SQL Azure 中的防火墙确实是一个很好的工具,但它不应该依赖其本身。还应使用具有强密码和使用特定权限配置的用户帐户来完善您的数据安全模型。

这些新工具可以使 SQL Azure 成为对基于云的应用程序非常严密的安全托管平台。如果您第一次试用此服务,请记住,必须对 SQL Azure 防火墙进行初始化配置后,才可以进行连接。必须首先通过 SQL Azure Web 门户完成此操作,但是如上所述,稍后便可直接在主数据库中进行。

不变性和内存资源

什么是不变性?不变性在面向对象的编程中仅意味着在初始创建对象后无法修改其状态。Microsoft .NET Framework 中的一个具体示例是字符串类。在代码中更改了字符串的值后,只需弃用内存中的原始字符串并创建一个用于存储新值的新字符串对象。

为什么从安全性的角度讲这很重要?只要服务器处于联机状态并且不重新启动,该字符串可以始终保留在内存中。您真的无法确定字符串将在内存中保留多长时间。这在考虑如何在代码中存储信息(例如,加密密钥或加密和解密数据的副本)时非常重要。内存中数据遗迹很可能留下将秘密透露给狡猾的数据盗贼的信息。

由于此不变性,强烈建议将此类数据存储在缓冲区(如字节数组)中。这样,当您使用此信息完成操作后,您可以使用 0 或任何其他数据覆盖该缓冲区来确保该数据不再位于内存中。

由于 Windows Azure 是云环境,曾经有人问我,是否仍需要考虑这些,这是一个很好的问题。仍需要考虑这些,在 Windows Azure 系统中,各个应用程序之间相互独立。这通常会使透露内存中的数据变得异常简单。很难将应用程序与云中的内存空间相关联。但是,我仍建议使用此方法时要格外谨慎并在执行操作后进行清理。您可能不会始终在云中运行此段代码,且其他漏洞可能会在将来将自身公开。不需要太担心,保持这个习惯并坚持按此方法执行操作即可。

图 3 中,我已修改了生成随机整数的上一示例。我在此处添加了一些错误处理以确保具有在任何情况下都始终运行的 finally 块。我正在该块中通过字节数组中的值执行非常简单的迭代,使用零值覆盖各个位置。这将覆盖内存中的数据,因为字节数组具有可变性。我知道该数字不再包含在归此成员执行的操作所有的内存中。可对用作项目(例如密钥、初始化向量和加密或解密数据)的数据缓冲区的任何字节数组执行此操作。

图 3 清除内存中的数据

public static int GenerateRandomNumber() {
  byte[] GeneratedBytes = null;

  try {
    GeneratedBytes = new byte[4];
    RNGCryptoServiceProvider CSP = 
      new RNGCryptoServiceProvider();
    CSP.GetBytes(GeneratedBytes);
    return BitConverter.ToInt32(GeneratedBytes, 0);
  }
  finally {
    for (int x = 0; x < GeneratedBytes.Length; x++) { 
      GeneratedBytes[x] = 0;
    }
  }
}

消息队列

Windows Azure 队列为 Microsoft 消息队列 (MSMQ) 服务提供了一组类似的功能,这些服务对企业 Windows 应用程序通用。Windows Azure 中的此消息队列服务以先进先出 (FIFO) 的方式存储基于文本的、大小不超过 8KB 的消息。这允许在不同的服务器(或像本示例这样,在云中)运行服务和应用程序以采用安全和分发式方法进行交互并相互之间发送可操作消息。

有 5 个允许您将消息推入到队列、查看消息、推出消息等等的基本功能。最常提出的问题是这些消息到底有多安全?

当前受 MSMQ 支持的许多功能在 Windows Azure 消息传送 API 中还不受支持。然而,存在相似之处。对于 blob 数据服务,消息传送服务利用相同的 REST get 和 put 界面。可在代码中或使用 URI 和 Web 请求调用编写和读取消息,可使用 SSL 针对不安全网络中的请求对该调用进行加密。这表示传送请求的过程是经过加密的。

另外,对于 Windows Azure 中的其他存储服务,对消息队列的任何访问必须利用同一存储服务密钥。只有有权访问该密钥的应用程序才能查看这些队列或向这些队列中添加消息。这使对这些消息的其他加密操作有点多余,除非这些消息的主体将保留安全网络或安全应用程序空间。

总结

在目前面向服务的体系结构和解决方案的趋势之下,很少人会想到在云应用程序中开展业务。多租户环境(例如 Windows Azure)中的数据和服务的隔离是放眼于使用私有数据的人们最关注的问题之一。

与所有新平台一样,安全和加密功能将继续在 Windows Azure 平台中不断改进。Microsoft 花费了巨大精力在以下方面:不仅提供安全、隔离环境,还要公开针对这些度量的公共证书所得成果。这应该会使工程师坚信,Microsoft 希望成为安全性和保持系统和应用程序为锁定状态方面的亲密合作伙伴。

安全,特别是加密的关键之处在于使您的信息和过程很难访问。我们可以将“很难”定义为超出了任何敌方在数据或进程的生命期内进入该系统的能力。然而,这是一种基于正在使用的应用程序或数据的要求的相对定义。这就是我为何在这篇文章中继续强调需要不断对安全和加密要求进行评估。这对确保可有效地使用这些工具以保护云系统的安全并保护数据至关重要。

Jonathan Wiggs 目前是 Nuance Communications Inc 的总工程师兼开发经理。您可通过 阅读他的博客或直接通过 Jon_Wiggs@yahoo.com 与 Wiggs 联系。