在运营 AWS EC2 时,您会经常看到 IMDSv1 和 IMDSv2 这两个术语。
IMDS 是 Instance Metadata Service 的缩写,它是一种允许 EC2 实例从内部查询自身元数据的服务。
例如,在 EC2 内部可以查询以下信息:
- 实例 ID
- AMI ID
- 区域
- 可用区
- 网络信息
- IAM 角色名称
- IAM 角色临时凭证
其中,从安全角度来看最重要的是 IAM 角色临时凭证。
如果 EC2 附加了 IAM 角色,应用程序就不需要将 Access Key 直接存储在代码或环境变量中。
相反,它可以通过 IMDS 获取临时凭证来调用 AWS API。
这种结构本身是一个非常好的安全设计。
但是,如果应用程序存在 SSRF 等漏洞,情况就会有所不同。

1. IMDS 位于何处?
在 EC2 实例内部,可以通过以下链接本地地址访问 IMDS:
http://169.254.169.254/latest/meta-data/
例如,要查询实例 ID,可以执行以下操作:
curl http://169.254.169.254/latest/meta-data/instance-id
如果实例附加了 IAM 角色,可以通过以下路径查看角色名称:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
知道角色名称后,就可以查询该角色的临时凭证。
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
此处返回的信息包括以下值:
- AccessKeyId
- SecretAccessKey
- Token
- Expiration
也就是说,在 EC2 内部运行的应用程序可以通过 IMDS 获取调用 AWS API 所需的临时凭证。
2. IMDSv1 的工作方式
IMDSv1 的结构非常简单。
无需单独的令牌,只需通过 HTTP GET 请求即可查询元数据。
示例如下:
curl http://169.254.169.254/latest/meta-data/
IAM 角色临时凭证也可以如下查询:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
IMDSv1 的优点是简单性和兼容性。
它可以在旧版 AWS SDK、旧版代理和旧版脚本中轻松运行。
但是,从安全角度来看,这种简单性可能会成为问题。
3. IMDSv1 的安全问题
IMDSv1 最大的问题是请求不需要单独的令牌。
IMDS 最初是只能从 EC2 实例内部访问的服务。
因此,外部用户无法直接从互联网访问 169.254.169.254。
但是,如果 Web 应用程序存在 SSRF 漏洞,攻击者可以使应用程序服务器代表其访问 IMDS。
例如,假设某个 Web 服务具有一项功能,当您输入 URL 时,服务器会代为获取该 URL。
https://example.com/fetch?url=http://example.org/image.png
如果此功能在没有 URL 验证的情况下运行,攻击者可以发出如下请求:
https://example.com/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/
在这种情况下,外部攻击者并非直接访问 IMDS,而是 EC2 内部的 Web 应用程序代为访问 IMDS。
这是 SSRF 与 IMDSv1 结合时变得危险的典型场景。
4. SSRF 与 IMDSv1 结合为何危险?
如果攻击者可以通过 SSRF 访问 IMDS,以下信息可能会被泄露:
- 附加到 EC2 的 IAM 角色名称
- 临时 Access Key
- 临时 Secret Key
- Session Token
- 凭证过期时间
此时,如果 EC2 角色被授予了过多的权限,损害范围就会扩大。
例如,如果角色包含以下权限,则非常危险:
- S3 完全访问
- Secrets Manager 完全查询
- DynamoDB 完全访问
- EC2 创建/删除权限
- IAM PassRole
- 管理员权限
归根结底,问题不仅仅是“是否使用了 IMDSv1”。
从实际安全事件的角度来看,以下因素共同作用:
- 应用程序是否存在 SSRF 漏洞?
- EC2 是否允许 IMDSv1?
- EC2 角色权限是否过多?
- 是否未在网络或应用程序级别阻止对元数据地址的访问?
5. IMDSv2 的工作方式
IMDSv2 是为了减少 IMDSv1 的安全弱点而引入的方式。
核心是基于令牌的会话。
在 IMDSv2 中,不能直接查询元数据。
首先,必须通过 PUT 请求获取令牌。
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token"
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
然后,必须在元数据请求中将令牌作为标头包含。
curl -H "X-aws-ec2-metadata-token: $TOKEN"
http://169.254.169.254/latest/meta-data/instance-id
IAM 角色凭证查询也是如此。
ROLE_NAME=$(curl -H "X-aws-ec2-metadata-token: $TOKEN"
http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -H "X-aws-ec2-metadata-token: $TOKEN"
http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
也就是说,IMDSv2 需要以下两个步骤:
- 通过 PUT 请求获取令牌
- 将获取到的令牌放入标头并查询元数据
6. IMDSv2 增强安全性的方式
IMDSv2 通过以下方式提高攻击难度:
1) 阻止简单的 GET 请求
在 IMDSv1 中,只需通过 GET 请求即可获取元数据。
curl http://169.254.169.254/latest/meta-data/
但在 IMDSv2 Required 状态下,没有令牌的请求会失败。
这意味着,如果 SSRF 漏洞仅允许操纵 URL,则很难绕过 IMDSv2。
2) 需要 PUT 请求
IMDSv2 令牌是通过 PUT 请求获取的。
许多 SSRF 漏洞只允许简单的 GET 请求,或者不允许更改 HTTP 方法。
在这种情况下,攻击者很难获取令牌。
3) 需要自定义标头
在 IMDSv2 中,查询元数据时需要以下标头:
X-aws-ec2-metadata-token: <TOKEN>
对于仅允许操纵 URL 的 SSRF 漏洞,很难添加此标头。
4) 跳数限制控制
IMDSv2 具有 HttpPutResponseHopLimit 设置。
此值限制了 IMDSv2 令牌响应可以经过的网络跳数。
在典型的独立 EC2 环境中,可以使用 1。
但在容器环境中,网络路径可能会增加一个跳数,因此可能需要 2。
7. IMDSv1 与 IMDSv2 比较
| 类别 | IMDSv1 | IMDSv2 |
|---|---|---|
| 元数据查询方式 | GET 请求 | PUT 获取令牌后进行 GET 请求 |
| 是否需要令牌 | 否 | 是 |
| SSRF 防御能力 | 低 | 相对较高 |
| 是否需要自定义标头 | 否 | 是 |
| HTTP 方法 | 主要是 GET | PUT + GET |
| 操作建议 | 用于旧版兼容 | 建议用于生产环境 |
| 设置值 | 可在 HttpTokens=optional 下使用 | 可通过 HttpTokens=required 强制执行 |
| 主要优点 | 简单性、兼容性 | 增强安全性 |
| 主要缺点 | 可能容易受到 SSRF 攻击 | 需要检查与旧版 SDK/Agent 的兼容性 |
—
8. EC2 元数据选项主要设置
| 设置 | 说明 | 建议 |
|---|---|---|
| HttpEndpoint | 是否使用 IMDS | 不需要则 disabled |
| HttpTokens | 是否要求 IMDSv2 令牌 | 建议 required |
| HttpPutResponseHopLimit | IMDSv2 令牌响应跳数限制 | 普通 EC2 为 1,容器环境考虑 2 |
| InstanceMetadataTags | 是否将实例标签作为元数据公开 | 不需要则 disabled |
在生产环境中,通常建议遵循以下方向:
- 不需要 IMDS 的实例:HttpEndpoint disabled
- 需要 IMDS 的实例:HttpTokens required
- 普通 EC2:考虑 Hop Limit 1
- ECS/EKS/容器环境:考虑 Hop Limit 2
- 如果不需要查询实例标签:InstanceMetadataTags disabled
9. 检查当前 EC2 的 IMDS 设置
您可以使用 AWS CLI 检查当前实例的元数据选项。
aws ec2 describe-instances
--query 'Reservations[].Instances[].{
InstanceId:InstanceId,
State:State.Name,
HttpEndpoint:MetadataOptions.HttpEndpoint,
HttpTokens:MetadataOptions.HttpTokens,
HopLimit:MetadataOptions.HttpPutResponseHopLimit,
MetadataTags:MetadataOptions.InstanceMetadataTags
}'
--output table
查看结果中的 HttpTokens 值。
- optional: 允许 IMDSv1 和 IMDSv2
- required: 仅允许 IMDSv2
因此,如果 HttpTokens=optional,则从安全角度来看是需要考虑转换的对象。
10. 将现有 EC2 更改为 IMDSv2 Required
要将现有 EC2 实例更改为 IMDSv2 Required,可以使用以下命令:
aws ec2 modify-instance-metadata-options
--instance-id i-xxxxxxxxxxxxxxxxx
--http-tokens required
--http-endpoint enabled
更改后,使用以下命令进行确认:
aws ec2 describe-instances
--instance-ids i-xxxxxxxxxxxxxxxxx
--query 'Reservations[].Instances[].MetadataOptions'
--output json
如果在容器环境中需要将跳数限制设置为 2,可以按如下方式应用:
aws ec2 modify-instance-metadata-options
--instance-id i-xxxxxxxxxxxxxxxxx
--http-tokens required
--http-put-response-hop-limit 2
--http-endpoint enabled
11. 为新 EC2 实例默认应用 IMDSv2
在生产环境中,最好从一开始就配置新实例要求 IMDSv2,而不是在创建后进行修改。
您可以在启动模板中指定以下设置:
{
"MetadataOptions": {
"HttpTokens": "required",
"HttpEndpoint": "enabled",
"HttpPutResponseHopLimit": 2
}
}
您还可以使用 AWS CLI 设置账户/区域范围的默认值。
但是,如果现有应用程序、代理或 SDK 不支持 IMDSv2,可能会发生故障。
因此,在生产环境中,不应立即强制执行,而应首先检查 IMDSv1 的使用情况。
12. 检查 IMDSv1 使用情况
在转换为 IMDSv2 之前,需要确认是否存在仍在使用的 IMDSv1 进程。
典型的检查方法如下:
检查 CloudWatch MetadataNoToken 指标
MetadataNoToken 指标用于识别未经令牌访问 IMDS 的请求。
简而言之,它是一个可以确认是否发生了 IMDSv1 方式请求的指标。
如果此指标持续增长,则表示仍有应用程序或代理在使用 IMDSv1。
使用 IMDS Packet Analyzer
使用 AWS 提供的 IMDS Packet Analyzer 可以检查实例内部哪些进程正在调用 IMDSv1。
在转换之前,建议检查以下项目:
- 旧版 AWS CLI
- 旧版 AWS SDK
- 旧版 SSM Agent
- 旧版 CloudWatch Agent
- 手动编写的 curl 脚本
- 旧版应用程序库
13. 从安全角度推荐的转换流程
在生产环境中,按照以下顺序进行转换是安全的:
- 在整个 EC2 列表中识别 HttpTokens=optional 的实例
- 检查 CloudWatch MetadataNoToken 指标
- 使用 IMDS Packet Analyzer 确认调用 IMDSv1 的进程
- 更新 AWS CLI、SDK、SSM Agent、CloudWatch Agent
- 在开发环境中测试 IMDSv2 Required
- 应用于预发布环境
- 按顺序应用于生产实例
- 在启动模板、Auto Scaling 组、AMI 构建管道中反映 IMDSv2 Required
- 将新实例的默认值设置为 IMDSv2 Required
- 如果需要组织范围的强制执行,请审查 SCP 或账户默认值
14. 仅靠 IMDSv2 是否足够?
IMDSv2 是一个非常重要的防御机制。
但是,仅应用 IMDSv2 并不能消除所有风险。
从安全角度来看,应同时采取以下对策:
IAM 角色最小权限
通过 IMDS 可能被窃取的最重要信息是 IAM 角色临时凭证。
因此,EC2 角色应仅授予最小权限。
一个不好的示例如下:
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
一个好的示例是仅限制必要的服务和资源。
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::example-bucket/app/*"
]
}
SSRF 防御
Web 应用程序中接受外部 URL 并由服务器直接请求的功能应谨慎处理。
以下地址通常是阻止目标:
- 169.254.169.254
- fd00:ec2::254
- 127.0.0.1
- localhost
- RFC1918 私有 IP 范围
- 链接本地地址
- 内部 DNS 名称
- 云元数据端点
此外,如果发生重定向,最终目的地也必须再次验证。
还应考虑 DNS Rebinding 攻击。
容器环境权限分离
在 ECS 或 EKS 环境中,应检查容器是否可以使用 EC2 节点的实例配置文件权限。
如果可能,建议使用以下结构:
- ECS: 使用任务角色 (Task Role)
- EKS: 使用 IRSA 或 EKS Pod Identity
- 最小化节点角色权限
- 审查 Pod 或容器访问 IMDS 的必要性
如果容器可以访问节点的 IMDS 且节点角色权限过多,则容器被攻陷时可能会导致 AWS 账户权限的升级。
15. 从 CIS 基准角度看 IMDSv2
CIS AWS Foundations Benchmark 和云安全检查标准将 EC2 元数据服务设置视为重要的安全项。
特别是,可以从以下角度进行检查:
- 是否允许 IMDSv1?
- 是否应用了 IMDSv2 Required?
- EC2 角色是否被授予了过多的权限?
- 是否需要公开实例元数据标签?
- 容器工作负载中是否存在节点角色权限泄露的可能性?
自动化诊断脚本通常会检查 describe-instances 结果中的 MetadataOptions 值。
例如,可以根据以下标准进行诊断:
HttpTokens == required 이면 양호
HttpTokens == optional 이면 취약 또는 개선 필요
但是,在生产环境中,应用程序兼容性检查优先于简单地通过命令更改设置。
16. 实践结论
IMDSv1 是一种旧方法,仅通过简单的 GET 请求即可查询元数据。
因此,当与 SSRF 漏洞结合时,EC2 IAM 角色临时凭证可能会被泄露。
IMDSv2 使用基于令牌的会话。
首先,必须通过 PUT 请求获取令牌,然后后续请求中必须包含令牌标头。
由于这种结构,通过简单的 SSRF 攻击窃取元数据变得更加困难。
在生产环境中,建议遵循以下标准:
- 尽可能应用 IMDSv2 Required
- 对于不需要 IMDS 的实例,禁用 IMDS
- 以最小权限设计 EC2 IAM 角色
- 应用 SSRF 防御逻辑
- 在容器环境中,使用任务角色 (Task Role)、IRSA 或 EKS Pod Identity
- 在转换前,检查 MetadataNoToken 指标和代理兼容性
一句话总结如下:
IMDSv1 是一种以兼容性为中心的旧版方式,而
IMDSv2 是一种旨在降低 SSRF 和元数据窃取风险的安全增强方式。
在生产环境中,如果没有特殊原因,将 IMDSv2 Required 设置为默认值是更安全的。
参考链接
AWS EC2 User Guide – Instance Metadata Service
AWS EC2 User Guide – Configure instance metadata options
AWS Security Blog – Add defense in depth against SSRF vulnerabilities
AWS Security Blog – Get the full benefits of IMDSv2 and disable IMDSv1
发表回复