云原生应用的 Azure 安全性

提示

此内容摘自电子书《为 Azure 构建云原生 .NET 应用程序》,可在 .NET 文档上获取,也可作为免费可下载的 PDF 脱机阅读。

Cloud Native .NET apps for Azure eBook cover thumbnail.

与传统应用程序相比,云原生应用程序更易于保护,也更难保护。 缺点是,你需要保护更小的应用程序,并将更多的精力用于构建安全基础结构。 大多数服务部署中的编程语言和样式的异类性质也意味着你需要更多地关注来自许多不同提供商的安全公告。

另一方面,较小的服务(每个服务都有自己的数据存储)限制了攻击范围。 如果攻击者破坏一个系统,那么攻击者跳转到另一个系统可能比在单一应用程序中更困难。 进程边界是强边界。 此外,如果数据库备份被公开,则损害就更有限,因为该数据库只包含数据的一个子集,并且不太可能包含个人数据。

威胁建模

无论云原生应用程序的优点是否大于缺点,都必须遵循相同的整体安全思维。 安全和安全思维必须是开发和操作情景的每个步骤的一部分。 规划应用程序时询问以下问题:

  • 这些数据丢失会有什么影响?
  • 如何限制错误数据注入到此服务的破坏性?
  • 谁应有权访问这些数据?
  • 开发和发布过程是否有审核策略?

所有这些问题都是威胁建模过程的一部分。 该过程将尝试回答以下问题:系统面临的威胁是什么,这些威胁出现的可能性有多大,以及它们可能带来的破坏。

确定威胁列表后,你需要决定是否应缓解这些威胁。 有时,对威胁的规划不太可行且费用昂贵,因此不值得花费大量精力。 例如,某些国家级行动者可能会将更改注入到数百万设备使用的进程设计中。 现在,此代码不会在环 3 中运行某个代码段,而是在环 0 中运行。 此过程允许攻击者绕过虚拟机监控程序并在裸机计算机上运行攻击代码,从而允许对在该硬件上运行的所有虚拟机发起攻击。

如果没有显微镜和该处理器芯片设计的高级知识,遭更改的处理器将很难被检测到。 这种情况不太可能发生,而且缓解成本很高,因此可能没有任何威胁模型会建议为其构建 Exploit Protection。

为更有可能发生的威胁生成保护更具吸引力,如允许 Id 递增攻击的访问控制中断(在 URL 中用 Id=3 替换 Id=2)或 SQL 注入。 为这些威胁制定缓解措施非常合理,可防止令人尴尬的影响公司声誉的安全漏洞。

最低权限原则

计算机安全的一项初步理念是最小特权原则 (POLP)。 这实际上是任何形式安全的基本理念,无论是数字形式还是物理形式。 简而言之,原则是,任何用户或进程都应具有执行其任务所需的最小权限。

例如银行出纳:访问保险箱是一种不常见的活动。 因此,一般的出纳不能自行打开保险箱。 若要获取访问权限,他们需要通过银行经理来升级其请求,后者会执行额外的安全检查。

在计算机系统中,有一个极好的示例是用户连接到数据库的权限。 在许多情况下,只需使用单个用户帐户即可生成数据库结构并运行应用程序。 除极端情况外,运行应用程序的帐户不需要更新架构信息的能力。 应该有多个帐户来提供不同级别的权限。 应用程序应该只使用授予对表中数据的读取和写入访问权限的权限级别。 这种保护将消除旨在删除数据库表或引入恶意触发器的攻击。

构建云原生应用程序的几乎每个部分都可以从最小特权原则中受益。 在基于角色的访问控制 (RBAC) 中设置防火墙、网络安全组、角色和范围时,就会发现它起作用。

渗透测试

随着应用程序日益复杂,攻击途径的数量会以惊人的速度增加。 威胁建模有一些缺陷,因为它通常由生成系统的相同人员执行。 与许多开发人员难以构想用户交互和生成无法使用的用户界面一样,大多数开发人员都难以查看每个攻击途径。 构建系统的开发人员也可能不熟悉攻击方法,并错过一些重要内容。

渗透测试或“触笔测试”涉及引入外部行动者来尝试攻击系统。 这些攻击者可能是外部咨询公司或从其他业务部门获得良好安全知识的其他开发人员。 将全权委托他们试图破坏系统。 通常,他们会发现需要修补的大量安全漏洞。 有时,攻击途径是完全意想不到的,例如对 CEO 的网络钓鱼攻击。

Azure 本身不断遭受 Microsoft 内部黑客团队的攻击。 多年来,他们最先发现了数十个潜在的灾难性攻击途径,并在它们被外部利用之前关闭了这些途径。 目标越诱人,外部行动者就越有可能尝试利用它,并且世界上有一些目标比 Azure 更具吸引力。

监视

如果攻击者尝试渗透某个应用程序,应会出现一些相关警告。 通常,通过检查服务中的日志可以发现攻击。 攻击者会在成功前留下蛛丝马迹。 例如,尝试猜测密码的攻击者会向登录系统发出许多请求。 对登录系统进行监视可以检测到与典型访问模式不一致的奇怪模式。 此监视功能可以转换为警报,进而提醒操作人员激活某种类型的对策。 高度成熟的监视系统甚至可能会根据这些偏差主动采取措施,从而添加规则来阻止请求或限制响应。

保护生成

大多数情况下,安全被忽略的一个地方就是在生成过程中。 不仅生成应运行安全检查(如扫描不安全代码或签入凭据),生成本身也应该是安全的。 如果生成服务器遭泄露,则它会提供一个极佳的途径来向产品引入任意代码。

假设攻击者希望窃取用户登录到 Web 应用程序的密码。 他们可能会引入一个生成步骤用于修改签出代码,以将任何登录请求映射到其他服务器。 下次代码通过生成时,它就会静默更新。 源代码漏洞扫描在生成之前运行时不会发现此漏洞。 同样,没有人会在代码评审中捕获它,因为生成步骤在生成服务器上运行。 被利用的代码将进入生产,在那里它可以获取密码。 可能没有生成过程更改的审核日志,或者至少没有任何人监视审核。

此场景是可用于侵入系统的看似低价值目标的极佳示例。 一旦攻击者破坏了系统的外围网络,他们就可以开始寻找提升其权限的方法,从而在任何他们需要的位置触发实质性的破坏。

生成安全代码

.NET Framework 已经是一个相当安全的框架。 它避免了某些非托管代码的缺陷,如遍历数组。 当发现安全漏洞时,我们会积极修复这些漏洞。 甚至还有一个 bug 赏金计划,它向研究人员支付报酬,鼓励他们发现框架中的问题并上报,而不是利用它们。

有多种方法可以使 .NET 代码更安全。 遵循准则(例如适用于 .NET 的安全编码准则一文)是从根本上确保代码安全的合理步骤。 OWASP top 10 是另一个可用于生成安全代码的重要指南。

在生成过程中,可以使用扫描工具来检测源代码中的问题,然后再将其投入生产。 大多数项目都与其他某些包存在依赖关系。 可以扫描过期包的工具将会在夜间生成中捕获问题。 即使在生成 Docker 映像时,检查并确保基本映像没有已知漏洞也是非常有用的。 要检查的另一件事是,没有人无意中签入了凭据。

内置安全性

Azure 旨在为大多数用户达成可用性与安全性之间的平衡。 不同用户会有不同的安全要求,因此他们需要对实现云安全的方法进行微调。 Microsoft 在信任中心发布了大量安全信息。 对于那些想要了解内置攻击缓解技术工作原理的专业人员,此资源应是第一站。

在 Azure 门户中,Azure 顾问是一种持续扫描环境并提出建议的系统。 其中的某些建议旨在节省用户资金,但其他建议用于标识可能不安全的配置,例如,使存储容器对外开放,而不受虚拟网络的保护。

Azure 网络基础结构

在本地部署环境中,大量精力用于设置网络。 设置路由器、交换机等是一项复杂的工作。 网络允许某些资源与其他资源通信,并在某些情况下阻止访问。 常见的网络规则是在开发环境中限制对生产环境的访问,以防开发到一半的代码运行出错并删除一系列数据。

开箱即用,大多数 PaaS Azure 资源仅具有最基本且最宽松的网络设置。 例如,Internet 上的任何人都可以访问应用服务。 新的 SQL Server 实例通常会受到限制,因此外部方无法访问它们,但 Azure 本身使用的 IP 地址范围是允许通过的。 因此,尽管 SQL 服务器免受外部威胁的影响,但攻击者只需设置一个 Azure 桥头,即可针对 Azure 上的所有 SQL 实例发起攻击。

幸运的是,大多数 Azure 资源都可以置于允许精细访问控制的 Azure 虚拟网络中。 与本地网络建立免遭外部环境影响的专用网络的方式类似,虚拟网络是位于 Azure 网络内部的专用 IP 地址孤岛。

Figure 9-1 A virtual network in Azure

图 9-1。 Azure 中的虚拟网络。

与本地网络具有管理网络访问权限的防火墙一样,你可以在虚拟网络边界建立类似的防火墙。 默认情况下,虚拟网络上的所有资源仍可与 Internet 通信。 只有传入连接需要某种形式的显式防火墙例外。

建立网络后,可以将存储帐户等内部资源设置为仅允许同样位于虚拟网络上的资源访问。 此防火墙提供额外的安全级别,如果该存储帐户的密钥泄露,攻击者将无法连接到它来利用泄露的密钥。 此方案是最小特权原则的另一个示例。

Azure Kubernetes 群集中的节点可以参与虚拟网络,就像其他 Azure 原生资源一样。 此功能称为 Azure 容器网络接口。 实际上,它会在虚拟网络中分配一个子网,将在其上分配虚拟机和容器映像。

继续说明最小特权原则,而不是虚拟网络中每个资源都需要与其他每个资源通信。 例如,在通过存储帐户和 SQL 数据库提供 Web API 的应用程序中,数据库和存储帐户不太可能需要相互通信。 它们之间的任何数据共享都会通过 Web 应用程序进行。 因此,可以使用网络安全组 (NSG) 拒绝这两个服务之间的流量。

拒绝资源间通信的策略可能很难实现,尤其是在没有流量限制的情况下使用 Azure 的背景下。 在其他一些云中,网络安全组的概念更为普遍。 例如,AWS 上的默认策略是,在 NSG 中的规则启用之前,资源无法相互通信。 虽然开发速度较慢,但更严格的环境提供了更安全的默认策略。 利用适当的 DevOps 做法(尤其是使用 Azure 资源管理器或 Terraform 来管理权限)可以更轻松地控制规则。

在本地资源和云资源之间设置通信时,虚拟网络也很有用。 虚拟专用网络可用于将两个网络无缝连接在一起。 对于所有用户都在现场的情况,此方法允许在没有任何网关的情况下运行虚拟网络。 有许多技术可用于建立此网络。 最简单的方法就是使用可在多个路由器和 Azure 之间建立的站点到站点 VPN。 流量通过 Internet 进行加密和隧道传输,每字节的成本与其他任何流量相同。 对于需要更多带宽或安全性的方案,Azure 提供名为 Express Route 的服务,该服务在本地网络与 Azure 之间使用专用线路。 建立该服务的成本更高难度更大,但也更安全。

用于限制对 Azure 资源的访问的基于角色的访问控制

RBAC 是一个系统,它为在 Azure 中运行的应用程序提供标识。 应用程序可以使用此标识来访问资源,而不是使用或额外使用密钥或密码。

安全主体

RBAC 的第一个组件是安全主体。 安全主体可以是用户、组、服务主体或托管标识。

Figure 9-2 Different types of security principals

图 9-2。 不同类型的安全主体。

  • 用户 - 任何在 Azure Active Directory 中拥有帐户的用户都是用户。
  • 组 - 来自 Azure Active Directory 的用户集合。 作为组成员,除了用户自己的角色外,用户还担任该组的角色。
  • 服务主体 - 运行服务或应用程序时所基于的安全标识。
  • 托管标识 - Azure 管理的 Azure Active Directory 标识。 开发管理凭据以向 Azure 服务进行身份验证的云应用程序时,通常使用托管标识。

安全主体可以应用于大多数资源。 这意味着可以将安全主体分配给在 Azure Kubernetes 中运行的容器,从而允许它访问存储在 Key Vault 中的机密。 Azure Function 可以获取权限,允许其与 Active Directory 实例对话,以验证调用用户的 JWT。 使用服务主体启用服务后,可以使用角色和范围精细管理其权限。

角色

安全主体可以担任许多角色,或者,用更合适的比喻来说,身兼多职。 每个角色定义一系列权限,例如“从 Azure 服务总线终结点读取消息”。 安全主体的有效权限集是分配给安全主体的所有角色的所有权限的组合。 Azure 具有大量内置角色,用户可以定义自己的角色。

Figure 9-3 RBAC role definitions

图 9-3。 RBAC 角色定义。

Azure 中还内置了许多高级角色,例如所有者、参与者、读者和用户帐户管理员。 使用“所有者”角色,安全主体可以访问所有资源,并将权限分配给其他人。 参与者对所有资源的访问权限级别相同,但无法分配权限。 读者只能查看现有 Azure 资源,而用户帐户管理员可以管理对 Azure 资源的访问权限。

更精细的内置角色(例如 DNS 区域参与者)的权限限于单个服务。 安全主体可以担任任意数量的角色。

作用域

角色可以应用于 Azure 中的一组受限资源。 例如,将范围应用到前面的从服务总线队列读取的示例中,你可以将权限缩小到单个队列:“从 Azure 服务总线终结点 blah.servicebus.windows.net/queue1 读取消息”

范围可以狭窄到单个资源,也可以应用于整个资源组、订阅甚至管理组。

测试安全主体是否具有特定权限时,将考虑角色和范围组合。 此组合提供强大的授权机制。

拒绝

以前,RBAC 只允许使用“允许”规则。 此行为使得某些范围的构建变得复杂。 例如,允许安全主体访问除一个存储帐户之外的所有存储帐户,该例外帐户需要授予对存储帐户的可能无限列表的显式权限。 每次创建一个新的存储帐户时,都必须将其添加到这个帐户列表中。 这增加了管理开销,当然不可取。

拒绝规则优先于允许规则。 现在表示相同的“全部允许(一个除外)”范围可以表示为两个规则“全部允许”和“拒绝此特定帐户”。 拒绝规则不仅可以简化管理,还允许通过拒绝对所有人的访问而获得额外安全性的资源。

检查访问

如你想象的那样,具有大量角色和范围会使找出服务主体的有效权限相当困难。 在此基础上再添加一些拒绝规则,只会增加复杂性。 幸运的是,有一个权限计算器可以显示任何服务主体的有效权限。 它通常位于门户的“IAM”选项卡下,如图 9-3 所示。

Figure 9-4 Permission calculator for an app service

图 9-4。 应用服务的权限计算器。

保护机密

密码和证书是攻击者的常见攻击途径。 密码破解硬件可能会进行暴力攻击,并尝试每秒猜测数十亿个密码。 因此,用于访问资源的密码必须非常强,包含大量字符。 这些密码正是几乎无法记住的密码类型。 幸运的是,Azure 中的密码实际上不需要被任何人知道。

许多安全专家建议,使用密码管理器来保存自己的密码是最佳方法。 虽然它将密码集中到一个位置,但它还允许使用高度复杂的密码,并确保它们对于每个帐户都是唯一的。 Azure 中存在相同的系统:机密的中心存储。

Azure Key Vault

Azure Key Vault 提供了一个集中位置,用于存储数据库、API 密钥和证书之类的密码。 在保管库中输入了机密后,就不会再次显示,提取和查看它的命令也有意变得复杂。 可以使用软件加密或 FIPS 140-2 2 级验证的硬件安全模块保护安全信息。

通过 RBAC 提供对密钥保管库的访问,意味着不是任何用户都可以访问保管库中的信息。 假设 Web 应用程序希望访问存储在 Azure Key Vault 中的数据库连接字符串。 若要获取访问权限,应用程序需要使用服务主体运行。 在此假定的角色下,它们可以安全读取机密。 有许多不同的安全设置可以进一步限制应用程序对保管库的访问,使其不能更新机密,而只读取它们。

可以监视对密钥保管库的访问,以确保只有预期的应用程序访问保管库。 日志可以集成回 Azure Monitor,从而在遇到意外情况时解锁设置警报的功能。

Kubernetes

在 Kubernetes 中,有一个类似的服务,用于维护少量的机密信息。 可以通过典型的 kubectl 可执行文件设置 Kubernetes 机密。

创建机密非常简单,只需查找要存储的值的 base64 版本:

echo -n 'admin' | base64
YWRtaW4=
echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

然后,将其添加到名为 secret.yml 的机密文件中,如以下示例所示:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm

最后,可以通过运行以下命令将此文件加载到 Kubernetes 中:

kubectl apply -f ./secret.yaml

然后,可以将这些机密装载到卷中,或通过环境变量向容器进程公开。 用于生成应用程序的十二因素应用方法建议使用最小公分母将设置传输到应用程序。 环境变量是最小的公分母,因为无论操作系统还是应用程序都支持环境变量。

使用内置 Kubernetes 机密的替代方法是从 Kubernetes 内访问 Azure Key Vault 中的机密。 要执行此操作,最简单的方法是将 RBAC 角色分配给想要加载机密的容器。 然后,应用程序可以使用 Azure Key Vault API 来访问机密。 但是,这种方法需要修改代码,而不遵循使用环境变量的模式。 相反,可以将值注入到容器中。 此方法实际上比直接使用 Kubernetes 机密更为安全,因为它们可由群集上的用户访问。

传输加密和静态加密

不管数据是在磁盘上还是在不同服务之间传输,都必须确保数据安全。 要防止数据泄露,最有效的方法是将其加密为其他人无法轻易读取的格式。 Azure 支持各种加密选项。

传输中

在 Azure 中,有几种加密网络流量的方法。 通常通过使用传输层安全性 (TLS) 的连接对 Azure 服务进行访问。 例如,与 Azure API 建立的所有连接都需要 TLS 连接。 同样,与 Azure 存储中的终结点的连接可以限制为只能通过 TLS 加密连接工作。

TLS 是一种复杂的协议,仅仅知道连接使用 TLS 并不足以确保安全性。 例如,TLS 1.0 是长期不安全的,而 TLS 1.1 也没有多好。 即使在 TLS 的版本中,也可以通过各种设置来更轻松地对连接进行解密。 最佳做法是检查服务器连接是否正在使用最新且配置良好的协议。

此检查可通过外部服务完成,如 SSL 实验室的 SSL 服务器测试。 针对典型 Azure 终结点(在本例中为服务总线终结点)的测试运行将生成接近 A 的完美分数。

甚至 Azure SQL 数据库之类的服务也使用 TLS 加密来使数据保持隐藏状态。 使用 TLS 加密传输中的数据的有趣之处在于,即使是 Microsoft,也无法侦听运行 TLS 的计算机之间的连接。 这应该为关心以下问题的公司带来慰藉:他们的数据可能会受到 Microsoft 本身的威胁,甚至是一个拥有比标准攻击者更多资源的国家行动者的威胁。

Figure 9-5 SSL labs report showing a score of A for a Service Bus endpoint.

图 9-5。 SSL 实验室报表显示服务总线终结点的分数为 A。

虽然这一级别的加密并不能总能满足要求,但它也会让你相信 Azure TLS 连接相当安全。 随着加密技术的改进,Azure 将继续发展其安全标准。 很高兴知道有人在关注安全标准,并在它们改进时更新 Azure。

静态

在任何应用程序中,磁盘上都有许多存放数据的地方。 应用程序代码本身是从某种存储机制加载的。 大多数应用程序还使用某种类型的数据库,例如 SQL Server、Cosmos DB,甚至是极具性价比的表存储。 这些数据库都使用高度加密的存储,以确保只有具有适当权限的应用程序才能读取你的数据。 即使系统操作员也不能读取已加密的数据。 因此,客户可以放心地保密信息。

存储

大多数 Azure 的基础是 Azure 存储引擎。 虚拟机磁盘装载在 Azure 存储之上。 Azure Kubernetes 服务在虚拟机上运行,这些虚拟机本身托管在 Azure 存储上。 甚至无服务器技术(例如 Azure Functions 应用和 Azure 容器实例)也会耗尽 Azure 存储的一部分磁盘。

如果 Azure 存储进行了很好的加密,则它为其他大部分需要加密的内容提供了基础。 Azure 存储是通过符合 FIPS 140-2256 位 AES 进行加密的。 这是一种备受推崇的加密技术,在过去 20 年左右的时间里,它一直是学术界广泛关注的主题。 目前,没有任何已知的实际攻击会使不知道密钥的人员读取通过 AES 加密的数据。

默认情况下,用于加密 Azure 存储的密钥由 Microsoft 管理。 提供了广泛的保护措施,以确保防止恶意访问这些密钥。 不过,具有特定加密要求的用户也可以提供自己的存储密钥,这些密钥在 Azure Key Vault 中管理。 这些密钥可以随时撤销,这将有效地使使用它们的存储帐户的内容不可访问。

虚拟机使用加密存储,但通过使用 BitLocker (Windows) 或 DM-Crypt (Linux) 等技术,可以提供另一层加密。 这些技术意味着,即使磁盘映像是从存储中泄露的,也几乎无法读取它。

Azure SQL

托管在 Azure SQL 上的数据库使用称为透明数据加密 (TDE) 的技术来确保数据保持加密。 默认情况下,它在所有新创建的 SQL 数据库上处于启用状态,但必须为旧数据库手动启用。 TDE 不仅对数据库执行实时加密和解密,还对备份和事务日志执行实时加密和解密。

加密参数存储在 master 数据库中,并在启动时将其读入内存以进行其余操作。 这意味着 master 数据库必须保持未加密状态。 实际密钥由 Microsoft 管理。 但是,具有严格安全要求的用户可能会在 Key Vault 中提供自己的密钥,就像在 Azure 存储中所做的那样。 Key Vault 提供密钥轮换和吊销等服务。

TDS 的“透明”部分来自这样一个事实,即使用加密数据库不需要对客户端进行更改。 虽然这种方法提供了良好的安全性,但泄漏数据库密码足以让用户对数据进行解密。 还有另一种方法可加密数据库中的单个列或表。 Always Encrypted 确保加密的数据在任何时候都不会以纯文本形式出现在数据库中。

设置此加密层需要在 SQL Server Management Studio 中通过向导运行,以选择加密的类型以及在 Key Vault 中存储关联密钥的位置。

Figure 9-6 Selecting columns in a table to be encrypted using Always Encrypted

图 9-6。 选择表中要使用 Always Encrypted 加密的列。

从这些加密列读取信息的客户端应用程序需要特别的权限来读取加密数据。 需要用 Column Encryption Setting=Enabled 更新连接字符串,并且必须从 Key Vault 检索客户端凭据。 然后,必须使用列加密密钥使 SQL Server 客户端整装待发。 完成后,剩余的操作使用到 SQL Client 的标准接口。 也就是说,像 Dapper 和实体框架这样构建在 SQL Client 之上的工具将继续工作,而无需更改。 对于每种语言的每一个 SQL Server 驱动程序,Always Encrypted 可能尚不可用。

TDE 和 Always Encrypted 组合(两者均可与客户端特定的密钥结合使用)可确保支持最严格的加密要求。

Cosmos DB

Cosmos DB 是 Microsoft 在 Azure 中提供的最新数据库。 它是基于安全性和加密从头开始构建的。 对于所有 Cosmos DB 数据库,AES-256 位加密是标准加密,不能禁用。 再加上对通信的 TLS 1.2 要求,整个存储解决方案都是加密的。

Figure 9-7 The flow of data encryption within Cosmos DB

图 9-7。 Cosmos DB 中的数据加密流。

虽然 Cosmos DB 不旨在提供客户加密密钥,但团队已经完成了一项重要的工作,确保它在不提供该密钥的情况下仍然符合 PCI DSS。 Cosmos DB 还不支持类似于 Azure SQL Always Encrypted 的任何类型的单列加密。

保持安全

Azure 提供了发布高度安全的产品所需的所有工具。 然而,链的强度取决于它最薄弱的环节。 如果部署在 Azure 之上的应用程序在开发时没有适当的安全意识和良好的安全审核,那么它们就会成为链上的薄弱环节。 可以使用很多极佳的静态分析工具、加密库和安全做法,以确保 Azure 上安装的软件与 Azure 本身一样安全。 示例包括静态分析工具加密库安全做法