←新闻详情

The New Stack更新于 10月30日 14:13

AI 智能体早已下线,它的访问密钥为何还活着?

What happens when the agent is long gone, but its access key lives on?

AI 导读

OWASP 将非人类身份的不当下线列为 2025 年 Top 10 风险之首,而 AWS IAM 用户访问密钥不会自动过期,被遗忘的密钥会一直有效直到有人手动禁用或删除。文章给出三步治理方案:用 CloudQuery 查询找出 90 天以上未使用且仍激活的密钥,用 OPA 策略在 Terraform/OpenTofu 计划阶段拦截长期凭证创建,并为每个密钥设定有限生命周期。

正文 · AI 翻译

设想一下:凭证报告上有一行记录,平台团队里没人能解释清楚。一个名为 research-agent-2 的 IAM 用户,其访问密钥创建于 14 个月前,最后一次使用是在 11 个月前。而它仍然处于激活状态。

它所支持的项目早已关闭,批准它的人甚至已经不在公司工作了。

虽然这个例子是假设的,但生命周期问题并非假设。

这并不需要粗心大意。为某项任务创建了一个智能体,任务结束了,却没有人重新审视它的访问权限。人员离职会触发离职流程;而一个已完成的智能体只是留下一个关闭的标签页。AWS IAM 用户访问密钥不会自动过期,因此被遗忘的密钥会一直有效,直到有人将其禁用或删除。

“人员离职会触发离职流程;而一个已完成的智能体只是留下一个关闭的标签页。”

OWASP 在其 2025 年非人类身份 Top 10 中将不当下线列为首位,而其补救措施读起来就像本系列的前提:淘汰不再需要的东西,并给其余的东西指定一个负责任的负责人。困难之处在于知道存在什么。

问题在于,攻击者的行动速度比年度审计快得多。2026 年 9 月,GreyNoise 记录了一场由 AI 编排的针对 PaperCut 服务器的攻击活动:攻击者从空工作区到在真实受害者处实现远程代码执行,用时不到四小时,而且一旦启动,该活动在 26 秒内攻陷了至少 11 个组织。那不是一次使用废弃 AWS IAM 密钥的攻击。它表明,一旦攻击者进入,暴露的访问权限会多么迅速地变得有用,以及为什么缓慢的盘点和吊销是危险的。

除非有人治理智能体的生命周期,否则它的访问权限可能比其目的存续得更久。

最后三篇文章涵盖了在发现时附加负责人、在最后工作日之前重新分配负责人,以及当负责人是智能体时会发生什么。凭证增加了一个相关问题,但有一个区别。智能体的部署身份是用于向云账户验证它或部署平台的身份。

“一台被遗忘的 VM 会花钱;一个被遗忘的凭证可以授予访问权限。”

已配置的凭证是部署为其他工作负载创建的持久密钥、机密或令牌;两者有不同的负责人和吊销程序。一台被遗忘的 VM 会花钱;一个被遗忘的凭证可以授予访问权限。补救措施仍然是一个查询、一个策略和一个时钟,各自用于解决它能解决的那部分问题。

1. 找出无人使用的密钥的查询

当源配置中启用该表时,CloudQuery 可以将 AWS IAM 凭证报告同步到 aws_iam_credential_reports。该报告为每个用户列出最多两个访问密钥槽位。这个查询是为 CloudQuery Platform 使用的 ClickHouse 方言编写的,它取每个报告的最新同步行,将槽位转换为行,并返回至少 90 天前创建且至少 90 天未使用的活跃 IAM 用户密钥。

SQL WITH latest_reports AS ( SELECT _cq_platform_id, arn, `user` AS principal, access_key1_active, access_key_1_last_rotated, access_key_1_last_used_date, access_key2_active, access_key_2_last_rotated, access_key_2_last_used_date FROM aws_iam_credential_reports ORDER BY _cq_sync_group_id DESC, _cq_sync_time DESC LIMIT 1 BY _cq_platform_id ), keys AS ( SELECT _cq_platform_id, arn, principal, 1 AS key_slot, access_key1_active AS active, access_key_1_last_rotated AS created, access_key_1_last_used_date AS last_used FROM latest_reports UNION ALL SELECT _cq_platform_id, arn, principal, 2 AS key_slot, access_key2_active AS active, access_key_2_last_rotated AS created, access_key_2_last_used_date AS last_used FROM latest_reports ) SELECT _cq_platform_id, arn, principal, key_slot, created, last_used FROM keys WHERE principal != ' ' AND active = true AND created ' ' AND active AND created 0 every _, m in input.plan.modules { tf_plan(m) } }

resource_changes := object.get(input.plan, "resource_changes", []) if standard_plan

resource_changes := [rc | some _, m in input.plan.modules some rc in object.get(m, "resource_changes", []) ] if run_all_plan

deny contains msg if { not standard_plan not run_all_plan msg := "Unsupported plan format for the credential policy" }

deny contains msg if { some rc in resource_changes key_resources[rc.type] "create" in rc.change.actions msg := sprintf("%s: long-lived credential prohibited", [rc.address]) }

allow if count(deny) == 0

该策略读取标准 Terraform 或 OpenTofu 计划,或 Terragrunt run-all 的模块映射。没有变更的计划可以省略 resource_changes,因此空计划被视为有效,而无法识别的形态会被拒绝而不是静默允许;只将其附加到模板会生成这些格式的项目上,因为 Pulumi、CloudFormation 和 Kubernetes 部署都会被拒绝。替换包含 create 操作,因此会被捕获。资源列表只是一个示例,该策略无法看到硬编码的机密或在 Terraform 之外生成的密钥。

allow 规则很重要:有了它,任何未被拒绝的内容都无需人工批准即可继续;没有它,一个不返回任何内容的策略会默认为待定,每次部署都要等待一个人。使用完整的 env zero 输入包装器进行测试,而不是裸的 terraform show -json 输出,覆盖密钥创建、仅角色计划、替换、run-all、缺失计划数据和销毁。ENV0_PRINT_POLICY_INPUT=true 会记录确切的输入;ENV0_OPA_VERSION 固定 OPA 版本。该策略在 env zero 的默认 OPA(0.46.1)和 1.x 上运行,这就是它使用 future.keywords 导入而不是 import rego.v1 的原因。

如果某个集成确实需要持久密钥,不要只是在另一个项目中创建它。使用有记录的例外:指定负责人、特定工作负载、最小权限、吊销截止日期、安全存储、监控和批准记录。IAM 访问密钥无法打标签,因此请在身份和凭证清单中记录所有权。

3. 确保给每个密钥一个有限的生命周期

AWS 不会为 IAM 用户访问密钥设置过期日期,因此结束日期必须来自其他地方。对于由 Terraform 管理的凭证,环境 TTL 提供了一个拆除时间点:将一次性凭证放入与其所服务的资源相同的环境中,并设置 TTL。但 TTL 并不是凭证过期。

它会启动销毁,只有当 Terraform 管理该密钥且销毁成功时,密钥才会消失。销毁失败、环境被锁定、手动创建的密钥或在其他地方管理的身份都会留下访问权限,因此要验证吊销情况,而不是相信倒计时。TTL 可以延长或设置为无限,而且 TTL 限制对管理员没有约束力。在免费计划中,一旦组织在一个月内运行超过 250 次,TTL 就会被销毁;等到下个月初(UTC)计数重置,因此密钥最多可以比其 TTL 多存活一个月。

治理任何 OIDC 联合的信任关系和权限范围,并在签发系统中轮换持久令牌;销毁基础设施并不会吊销调用者。由 Terraform 管理的访问密钥还会将其秘密值记录在状态中,因此要保护状态后端。

对于任何环境生命周期之外的密钥,要在受控情况下将其退役。凭证报告不包含密钥 ID,因此要查找这些 ID,检查最后使用元数据,确认所有者和依赖关系,然后停用特定密钥:

Shell aws iam list-access-keys \ --user-name research-agent-2

aws iam get-access-key-last-used \ --access-key-id <key-id>

aws iam update-access-key \ --user-name research-agent-2 \ --access-key-id <key-id> \ --status Inactive

停用是可逆的,但并非无害:季度批处理或灾难恢复作业可能会在典型发布周期很久之后失败。在与工作负载计划相匹配的观察期内留意身份验证错误,获得所有者签字确认,然后按照变更程序删除,因为删除不可逆。

env zero 的免费计划包括自定义审批策略和环境 TTL,每月 250 次运行和 30 个活跃环境。CloudQuery 的平台有免费层级,其 CLI 可同步到你自己的 Postgres。同步凭证报告,运行查询,并统计行数。它是只读的,所以最坏的结果就是你看到了一份你不想看到的列表。

凭证需要一个结束日期

没有所有者的资源会一直花钱,直到有人注意到;而无主的凭证可能会向发现它的任何人授予非预期的访问权限。

“旧密钥不需要被标记为孤立,攻击者才能使用它;它只需要有效即可。”

完成上个季度项目的智能体不会标记自己的凭证,而下一个遇到这些凭证的智能体可能不属于你的组织。旧密钥不需要被标记为孤立,攻击者才能使用它;它只需要有效即可。

文章《当智能体早已消失,但它的访问密钥仍然存在时会发生什么?》最初发表于 The New Stack。

来源

The New Stack