IAM失误如何导致数据泄露

大多数权限检查工具缺乏对授予的权限是否真正符合应用程序需求的深入理解。

译自 How IAM Missteps Cause Data Breaches,作者 Rak Siva。

当数据泄露和权限相关的安全事件不断发生时,这清楚地表明我们没有正确处理访问控制。让我们回顾一些泄露事件,并考虑拥有过于宽松的身份和访问管理 (IAM) 策略的后果,即使在那些似乎不太可能遭受攻击的环境中也是如此。

2019 年,Capital One 遭受数据泄露,导致超过 1 亿客户的个人数据泄露。这次攻击不需要复杂的社会工程或多种黑客工具;它始于一个配置错误的 Web 应用防火墙 (WAF),该防火墙允许攻击者访问 Amazon S3 的内部实例。主要问题是什么?IAM 角色设置得过于宽松,没有严格的知情访问权限。这使得攻击者能够从相对较小的访问点升级权限到主要数据资产。

2023 年,丰田披露了一起数据泄露事件,影响了包含私人客户信息的数据库和资源。根本问题是什么?IAM 策略在非生产环境中授予了过于广泛的访问权限,限制很少。这些权限未经检查,导致敏感资源暴露于公众。这些宽松策略背后的理由是非生产环境“风险较低”。

“云 IAM 系统的设计天生复杂,使安全成为大多数用户的艰难挑战。当阻力最小的路径是错误配置时,难怪很多人会陷入危险的设置。”

— DevOps 工程师 [Mat Duggan]

阻力最小的路径

很容易看出为什么会频繁发生这种情况,因为设置严格的 IAM 权限可能既费时又费力。假设我们在 AWS 中有一个云应用程序,它有多个服务需要独特的权限才能访问不同的 AWS 资源,例如用于存储的 S3、用于数据库的 DynamoDB 和用于消息传递的 SQS。推荐的方法是为每个服务创建定制的 IAM 角色,并确保个人、应用程序或系统仅拥有执行其任务所需的必要访问权限,从而通过以下方式降低安全风险:

  • 默认最小访问权限:仅为每个角色或应用程序授予必要的权限,即使在非生产环境中也要避免广泛访问。
  • 动态权限:随着角色和需求的变化,定期审查和调整权限,保持必要的最小访问权限。
  • 访问撤销:在不再需要时删除权限,以防止随着时间的推移出现“权限蔓延”。

如果发生数据泄露,授予最小权限的最大好处是降低了泄露的严重性。即使攻击者可以找到一种方法来使您的 Lambda 调用代码,如果该函数几乎没有任何权限,那么执行代码的能力所能造成的损害极其有限,从而最大限度地减少泄露的影响。

事情变得复杂的地方在这里。实施最小权限需要按需提供应用程序的需求规范,其中包含每个互连资源背后的层次结构和上下文详细信息。开发人员很少确切知道每项服务需要哪些权限。例如,要对 S3 存储桶执行读取操作,我们还需要列出 S3 存储桶内容的权限。

弄清楚所有这些可能需要反复试验、检查日志、更新角色并在每次出现缺少权限错误时重新部署。除此之外,某些服务可能需要间接权限。例如,如果服务 A 与服务 B 交互,则服务 A 可能需要访问服务 B 依赖的资源的权限。这可能导致整个链中的权限更广泛,从而难以清晰地隔离访问权限。

在快速发展的压力下,阻力最小的路径是授予广泛的访问权限,并以此为理由建议稍后会收紧权限。但是,这种情况往往不会发生。

仅仅被动应对是不够的

在这里,我们开始被动应对并应用扫描错误配置的工具。诸如 AWS IAM Access Analyzer 或 Google Cloud 的 IAM 建议者之类的工具对于识别危险的权限或潜在的权限过度使用非常有价值。但是,如果这些工具成为主要防御手段,则可能会产生虚假的安全感。

大多数权限检查工具的设计都是为了在某个时间点分析权限,通常在权限已经到位后才标记问题。这种被动的方法意味着只有在误配置发生之后才会解决问题,这使得系统在下次扫描之前一直处于脆弱状态。

实际上,它们只标记异常广泛的权限,并且缺乏上下文来评估更细微的配置并确定所需的最低访问级别。例如,IAM Access Analyzer 可以标记诸如 S3 存储桶可公开访问等问题。将此标记为需要检查的问题并没有错,但该工具缺乏对授予的权限是否真正符合应用程序需求的深入了解。

在这个例子中,如果我们可以提供正确的上下文,那么我们有以下选择:

  • PutObjectDeleteObject 等更敏感的操作限制为仅特定用户或角色。
  • 将访问权限限制为与受信任来源相对应的特定 IP 范围。
  • 在存储桶策略中设置条件,仅允许在特定时间访问,如果合适,则限制暴露窗口。

默认最小权限

解决方案在于重新思考我们最初建立这些关系的方式。让我们来看两段非常简单的代码,它们都公开了一个 API,其中包含一个路由,用于从云存储桶返回预签名 URL。

左侧的示例使用 FastAPI 框架,右侧的示例使用 Nitric 框架。这两个函数是等效的;它们将返回一个用于下载文件的预签名 URL。

关键的区别在于,Nitric 示例包含一个额外的声明,指示函数打算如何使用存储桶:.allow(“read”)。这个意图声明是生成两个资源之间关系层次结构所需的上下文,如果没有它,处理程序将无法访问存储桶。

虽然这种方法只需要最少的努力,但它代表了我们思考访问控制方式的显著转变。通过允许开发人员在其声明旁边直接指定他们预期的资源使用方式,他们可以清楚地表明他们希望应用程序使用该基础设施做什么。这种与声明的接近简化了上下文管理,因为开发人员不需要在脑海中映射整个系统。相反,在部署时,我们拥有每个应用程序所需的权限的精确记录。

更进一步,我们可以生成此关系图的 JSON 甚至可视化表示。

要查看其运行情况,请查看 Nitric 快速入门指南,该指南将引导您设置项目、创建新的堆栈以及生成诸如 Pulumi 或 Terraform 之类的基础设施即代码,这些代码默认情况下为您的应用程序授予最小权限。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注