小窍门
Azure 平台的云原生 .NET 应用电子书封面缩略图。
与传统应用程序相比,云原生应用程序既容易又难保护。 缺点是,你需要保护更小的应用程序,并投入更多精力来构建安全基础结构。 大多数服务部署中的编程语言和样式的异类性质也意味着你需要更加关注来自许多不同提供商的安全公告。
另一方面,较小的服务(每个服务都有自己的数据存储)会限制攻击的范围。 如果攻击者攻陷一个系统,与在单体应用程序中的情况相比,攻击者要跳转到另一个系统可能会更困难。 进程边界是强边界。 此外,如果数据库备份公开,则损害将更加有限,因为该数据库仅包含一部分数据,并且不太可能包含个人数据。
威胁建模
无论这一优点是否超过云原生应用程序的缺点,都必须遵循相同的整体安全思维模式。 安全和安全思维必须是开发和运营故事的每一步的一部分。 规划应用程序时询问以下问题:
- 此数据丢失的影响是什么?
- 如何限制向此服务注入不良数据造成的损害?
- 谁应该有权访问此数据?
- 开发和发布过程中是否有审核策略?
所有这些问题都是称为 威胁建模的过程的一部分。 此过程尝试回答系统存在哪些威胁、威胁的可能性以及它们的潜在损害的问题。
建立威胁列表后,需要确定它们是否值得缓解。 有时候,威胁如此不大可能且应对成本昂贵,以至于不值得为它耗费精力。 例如,某些状态级别执行组件可能会将更改注入到数百万台设备使用的进程的设计中。 现在,该代码在 Ring 0 中运行,而不是在 Ring 3 中运行一段代码。 此过程允许利用该漏洞绕过虚拟机监控程序并在裸机计算机上运行攻击代码,从而允许对在该硬件上运行的所有虚拟机的攻击。
没有显微镜并且缺乏该处理器硅设计的先进知识,很难检测到经过修改的处理器。 这种情况不太可能发生且成本高昂,因此可能没有威胁模型建议为其生成攻击保护。
更为常见的威胁,例如由于访问控制不当而允许的递增攻击(在 URL 中替换Id=2
Id=3
)或 SQL 注入,是构建防护措施时的更吸引人的目标。 这些威胁的缓解措施是相当合理的,可以建立和防止令人尴尬的安全漏洞,从而抹黑公司的声誉。
最低权限原则
计算机安全性的创始理念之一是最低特权原则(POLP)。 它实际上是大多数安全形式的一个基本概念,无论是数字安全还是物理安全。 简言之,原则是,任何用户或进程都应拥有尽可能少的权限来执行其任务。
例如,想想银行的柜员:进入保险箱是不常见的行为。 因此,一般柜员自己不能打开保险箱。 为了获得访问权限,他们需要通过执行其他安全检查的银行经理升级请求。
在计算机系统中,一个出色的示例是连接到数据库的用户的权限。 在许多情况下,有一个用户帐户用于生成数据库结构并运行应用程序。 除非在极端情况下,运行应用程序的帐户不需要更新架构信息。 应有多个帐户提供不同级别的特权。 应用程序应仅使用授予对表中数据的读取和写入访问权限的权限级别。 此类保护将消除旨在删除数据库表或引入恶意触发器的攻击。
构建云原生应用程序的几乎所有部分都可以受益于记住最低特权原则。 设置防火墙、网络安全组、角色和基于角色的访问控制(RBAC)中的作用域时,可以在其中找到它。
渗透测试
随着应用程序变得更加复杂,攻击途径的数量会以惊人的速度增加。 威胁建模存在缺陷,因为它往往由构建系统的同一人员执行。 与许多开发人员在设想用户交互时遇到困难,然后生成不可用的用户界面的方式相同,大多数开发人员都很难看到每个攻击途径。 此外,构建系统的开发人员可能不太熟悉攻击方法,并错过了一些关键内容。
渗透测试或“渗测”涉及引入外部攻击者试图攻击系统。 这些攻击者可能是来自业务其他部分的外部咨询公司或其他具有良好安全知识的开发人员。 他们被给予完全的自由去尝试颠覆系统。 他们经常会发现需要修补的大量安全漏洞。 有时攻击途径会完全出人意料,比如利用针对首席执行官的网络钓鱼攻击。
Azure 本身正在不断受到来自Microsoft内部黑客团队的攻击。 多年来,他们一直抢先发现数十种潜在的灾难性攻击途径,并在它们被外部利用之前将其关闭。 目标越诱人,外部参与者就越有可能试图利用它,而且世界上很少有目标比 Azure 更诱人。
监测
如果攻击者尝试渗透应用程序,应有一些警告。 通常,可以通过检查来自服务的日志来发现攻击。 攻击会留下明显的标志,这些标志在攻击成功之前可以被发现。 例如,尝试猜测密码的攻击者会对登录系统发出许多请求。 围绕登录系统的监视可以检测与典型访问模式不符的奇怪模式。 此监视可以转换为警报,进而可以提醒作人员激活某种对策。 高度成熟的监视系统甚至可能会基于这些偏差主动添加规则来阻止请求或限制响应。
保护构建
一个常被忽视安全性的方面是构建过程。 不仅构建应运行安全检查(例如扫描不安全代码或已签入凭据),而且构建本身也应是安全的。 如果生成服务器遭到入侵,则它提供了一个出色的向量,用于将任意代码引入产品。
假设攻击者希望窃取登录 Web 应用程序的人员的密码。 他们可以引入一个构建步骤,修改签出的代码,将任何登录请求镜像到另一台服务器。 下次代码完成生成时,它会以无提示方式更新。 源代码漏洞扫描不会捕获此漏洞,因为它在生成之前运行。 同样,没有人会在代码评审中捕获它,因为生成步骤位于生成服务器上。 被利用的代码将转到生产环境,可在其中获取密码。 可能没有生成过程更改的审核日志,或者至少没有监视审核的人。
此情形是一个看似低价值目标的完美例子,可以用于入侵系统。 一旦攻击者入侵系统外部,他们就可以开始逐步寻找提升权限的方法,从而在任意他们选择的位置造成严重危害。
生成安全代码
.NET Framework 已经是一个相当安全的框架。 它避免了非托管代码的一些陷阱,例如超出数组边界。 在发现安全漏洞时,会积极完成工作来修复安全漏洞。 甚至还有一个漏洞赏金计划,支付研究人员在框架中查找问题并报告,而不是利用它们。
可通过多种方式使 .NET 代码更安全。 遵循 .NET 安全编码准则 是确保代码自始至终安全的合理步骤。 OWASP十大安全风险列表是构建安全代码的另一个宝贵指南。
构建过程是使用扫描工具检测源代码中问题的好地方,因为这样可以在这些问题进入生产环境之前发现它们。 大多数项目都依赖于一些其他包。 可以扫描过时包的工具将捕获夜间构建中的问题。 即使生成 Docker 映像,检查并确保基础映像没有已知的漏洞也很有用。 要检查的另一件事是,没有人意外签入凭据。
内置安全性
Azure 旨在平衡大多数用户的可用性和安全性。 不同的用户将有不同的安全要求,因此他们需要微调其云安全性的方法。 Microsoft在 信任中心发布大量安全信息。 对于那些有兴趣了解内置攻击缓解技术工作原理的专业人士来说,此资源应该是第一站。
在 Azure 门户中, Azure 顾问 是一个系统,它不断扫描环境并提出建议。 其中一些建议旨在节省用户资金,但另一些建议旨在识别潜在的不安全配置,例如让存储容器向世界开放,不受虚拟网络保护。
Azure 网络基础结构
在本地部署环境中,大量的能源专用于设置网络。 设置路由器、交换机和此类工作非常复杂。 网络允许某些资源与其他资源通信,在某些情况下阻止访问。 常见的网络规则是限制开发环境访问生产环境,以防半开发的代码段出错从而删除大量数据。
开箱即用,大多数 PaaS Azure 资源只有最基本的和宽松的网络设置。 例如,Internet 上的任何人都可以访问应用服务。 新的 SQL Server 实例通常受到访问限制,因此外部实体无法访问它们,但 Azure 本身使用的 IP 地址范围是允许的。 因此,虽然 SQL 服务器受到外部威胁的保护,但攻击者只需设置 Azure 桥头,从中可以针对 Azure 上的所有 SQL 实例发起攻击。
幸运的是,大多数 Azure 资源都可以放置在允许精细访问控制的 Azure 虚拟网络中。 类似于本地网络建立受更广泛的世界保护的专用网络的方式,虚拟网络是位于 Azure 网络中的专用 IP 地址的岛。
图 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 模型中的第一个组件是安全主体。 安全主体可以是用户、组、服务主体或托管标识。
图 9-2. 不同类型的安全主体。
- 用户 - 在 Azure Active Directory 中拥有帐户的任何用户都是用户。
- 组 - Azure Active Directory 中的用户集合。 作为组的成员,用户除了自己的角色外,还负责该组的角色。
- 服务主体 - 运行服务或应用程序的安全标识。
- 托管标识 - 由 Azure 管理的 Azure Active Directory 标识。 云应用程序开发时通常使用托管标识来处理凭证,以便进行 Azure 服务身份验证。
安全主体可应用于大多数资源。 这一方面意味着,可以将安全主体分配给在 Azure Kubernetes 中运行的容器,使其能够访问 Key Vault 中存储的机密。 Azure 函数可以接受允许它与 Active Directory 实例通信的权限,以验证呼叫用户的 JWT。 使用服务主体启用服务后,可以通过使用角色和作用域来精细管理其权限。
角色
安全主体可以承担许多角色,或者,使用更讽刺的类比,戴许多帽子。 每个角色定义一系列权限,例如“从 Azure 服务总线终结点读取消息”。 安全主体的有效权限集是分配给安全主体拥有的所有角色的所有权限的组合。 Azure 具有大量内置角色,用户可以定义自己的角色。
图 9-3. RBAC 角色定义。
Azure 中内置了许多高级角色,例如所有者、贡献者、读者和用户帐户管理员。 使用“所有者”角色,安全主体可以访问所有资源并向其他人分配权限。 参与者对所有资源的访问权限级别相同,但无法分配权限。 读取者只能查看现有的 Azure 资源,用户帐户管理员可以管理对 Azure 资源的访问权限。
更精细的内置角色(如 DNS 区域参与者 )的权限仅限于单个服务。 安全主体可以承担任意数量的角色。
范围
角色可以应用于 Azure 中的一组受限资源。 例如,将范围应用于从服务总线队列读取的上一个示例,可以将权限缩小到单个队列:“从 Azure 服务总线终结点 blah.servicebus.windows.net/queue1
读取消息”
范围可以像单个资源一样窄,也可以应用于整个资源组、订阅甚至管理组。
测试安全主体是否具有特定权限时,将考虑角色和作用域的组合。 这种组合提供了强大的授权机制。
否认
以前,在 RBAC 中只允许使用“允许”规则。 这种行为使一些应用范围很难构建。 例如,允许安全主体访问所有存储帐户,但不包括一个,需要向潜在的无止境的存储帐户列表授予显式权限。 每次创建新存储帐户时,都必须将其添加到此帐户列表中。 这增加了肯定不需要的管理开销。
拒绝规则优先于允许规则。 现在,表示同一个“只允许一个”范围可以表示为两个规则“允许全部”和“拒绝此一个特定规则”。 拒绝规则不仅简化了管理,而且允许通过拒绝对每个人的访问来获得额外安全的资源。
检查访问权限
可以想象一下,拥有大量角色和范围可以使确定服务主体的有效权限相当困难。 将拒绝规则堆在上面,只是为了增加复杂性。 幸运的是,有一个 权限计算器 可以显示任何服务主体的有效权限。 它通常位于门户中的 IAM 选项卡下,如图 9-3 所示。
图 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 版本中,也有各种设置可以使连接更易于解密。 最佳措施是检查并确认服务器连接是否使用 up-to-date 和配置良好的协议。
此检查可由外部服务(如 SSL 实验室的 SSL 服务器测试)完成。 针对典型的 Azure 终结点(在本例中为服务总线终结点)运行的测试会生成接近完美的 A 分数。
即使是 Azure SQL 数据库等服务也使用 TLS 加密来隐藏数据。 有关使用 TLS 加密传输中的数据的有趣部分是,即使Microsoft也不可能侦听运行 TLS 的计算机之间的连接。 这应该为担忧数据安全的公司提供安慰,因为他们的数据可能面临来自微软本身的风险,甚至可能遭到拥有比一般攻击者更多资源的国家行为者的威胁。
图 9-5. SSL Labs 报告显示服务总线终结点的评分为 A。
虽然这种加密级别不会一直足够,但它应该激发人们对 Azure TLS 连接相当安全的置信度。 随着加密的改进,Azure 将继续改进其安全标准。 知道有专人在监控安全标准并在其改进时更新 Azure,真让人安心。
静态
在任何应用程序中,数据都位于磁盘上的多个位置。 应用程序代码本身是从某些存储机制加载的。 大多数应用程序还使用某种数据库,例如 SQL Server、Cosmos DB,甚至是令人惊讶地具有价格优势的表存储。 这些数据库都使用高度加密的存储,以确保除了具有适当权限的应用程序之外,任何人都无法读取数据。 即使是系统作员也无法读取已加密的数据。 因此,客户可以保持确信其机密信息仍为机密。
储存
Azure 的大部分基础是 Azure 存储引擎。 虚拟机磁盘装载在 Azure 存储之上。 Azure Kubernetes 服务在 Azure 存储上托管的虚拟机上运行。 即使是无服务器技术(如 Azure Functions 应用和 Azure 容器实例)也会耗尽属于 Azure 存储的磁盘。
如果 Azure 存储加密良好,则它为大多数其他内容都提供加密的基础。 Azure 存储使用符合 FIPS 140-2 的 256 位 AES进行加密。 这是一项备受推崇的加密技术,在过去20年左右,一直是广泛学术审查的主题。 目前,没有已知的实际攻击,允许不知道密钥的人读取 AES 加密的数据。
默认情况下,用于加密 Azure 存储的密钥由Microsoft管理。 有广泛的保护,以确保防止恶意访问这些密钥。 但是,具有特定加密要求的用户还可以提供自己在 Azure Key Vault 中管理的 存储密钥 。 可以随时撤销这些密钥,这将导致利用这些密钥访问的存储帐户内容无法访问。
虚拟机使用加密存储,但可以使用 Windows 上的 BitLocker 或 Linux 上的 DM-Crypt 等技术提供另一层加密。 这些技术意味着,即使磁盘映像从存储中泄露,它仍然几乎无法读取它。
Azure SQL
Azure SQL 上托管的数据库使用一种称为 透明数据加密(TDE) 的技术来确保数据保持加密。 默认情况下,它在所有新建的 SQL 数据库上都启用,但必须为旧数据库手动启用。 TDE 不仅对数据库以及备份和事务日志执行实时加密和解密。
加密参数存储在 master
数据库中,启动时会被读入内存,用于后续操作。 这意味着数据库 master
必须保持未加密状态。 实际密钥由Microsoft管理。 但是,有严格安全要求的用户可以在 Key Vault 中提供自己的密钥,这与 Azure 存储类似。 Key Vault 提供密钥轮换和吊销等服务。
TDS 的“透明”部分来自使用加密数据库不需要客户端更改的事实。 虽然此方法提供良好的安全性,但泄露数据库密码足以让用户能够解密数据。 还有一种方法可以加密数据库中的各个列或表。 Always Encrypted 可确保加密数据在数据库中任何时候都不会以纯文本形式出现。
设置此加密层需要通过 SQL Server Management Studio 中的向导运行,以选择加密类型以及 Key Vault 中存储关联密钥的位置。
图 9-6. 使用 Always Encrypted 选择要加密的表中的列。
从这些加密列读取信息的客户端应用程序需要对读取加密数据进行特殊限制。 连接字符串需要通过 Column Encryption Setting=Enabled
更新,并且必须从 Key Vault 中检索客户端凭据。 然后,必须为 SQL Server 客户端配置列加密密钥。 完成后,其余作将使用 SQL 客户端的标准接口。 也就是说,基于 SQL 客户端构建的 Dapper 和 Entity Framework 等工具将继续工作,而无需更改。 Always Encrypted 可能尚不适用于每种语言上的每个 SQL Server 驱动程序。
TDE 和 Always Encrypted 的组合,这两者都可用于特定于客户端的密钥,可确保即使是最精确的加密要求也受支持。
Cosmos DB (宇宙数据库)
Cosmos DB 是 Azure 中Microsoft提供的最新数据库。 它从头开始构建,考虑到安全和加密。 AES-256 位加密对所有 Cosmos DB 数据库都是标准的,不能禁用。 加上 TLS 1.2 通信要求,将加密整个存储解决方案。
图 9-7. Cosmos DB 中的数据加密流。
虽然 Cosmos DB 不提供提供客户加密密钥,但团队已经做了大量工作,以确保它保持 PCI-DSS 合规,而无需这样做。 Cosmos DB 也不支持与 Azure SQL 的 Always Encrypted 类似的任何类型的单列加密。
保持安全
Azure 提供了发布高度安全产品所需的所有工具。 然而,链条的强度只有其最薄弱的一环那么强。 如果部署在 Azure 之上的应用程序没有采用适当的安全思维模式和良好的安全审核进行开发,则它们将成为链中的薄弱环节。 有许多出色的静态分析工具、加密库和安全做法可用于确保安装在 Azure 上的软件与 Azure 本身一样安全。 示例包括 静态分析工具 和 加密库。