4. AWS S3 签名版本:Signature V2 与 Signature V4 的区别
AWS S3 提供了两种请求签名协议:Signature V2(已弃用)和 Signature V4(当前标准)。理解两者的差异是正确实现对象存储权限验证的基础。V4 签名不仅增强了安全性,还引入了区域和服务绑定的概念,彻底改变了请求的构造方式。
4.1 核心差异对比
| 对比维度 | Signature V2 | Signature V4 |
|---|---|---|
| 哈希算法 | HMAC-SHA1 | HMAC-SHA256 |
| 签名密钥派生 | 直接使用 AccessKey Secret | 基于 Secret + 日期 + 区域 + 服务 分层派生 |
| 请求头要求 | 仅需 Date 或 Expires | 必须包含 x-amz-date 和 Host;推荐包含 Content-SHA256 |
| Payload 校验 | 不校验请求体 | 可选校验(通过 x-amz-content-sha256) |
| 区域绑定 | 无 | 签名密钥绑定区域(如 us-east-1) |
| 服务绑定 | 无 | 签名密钥绑定服务(如 s3) |
| URL 过期签名 | Query 参数较少 | Query 参数更多,包含 Credential/SignedHeaders/Signature |
| 安全性 | 较低(易受重放攻击) | 较高(时间窗口 + 区域隔离) |
| 当前状态 | 已弃用(2020 年后新建存储桶不再支持) | 强制使用 |
迁移建议:所有新项目必须使用 Signature V4。如果现有系统仍在使用 V2,应尽快迁移,因为 AWS 已逐步关闭对 V2 的支持,且大多数兼容 S3 的对象存储(如 MinIO、Ceph)默认也推荐 V4。
4.2 V4 签名流程详解
Signature V4 的签名过程分为四个阶段,每一步都严格依赖前一步的输出。下面通过一个完整的流程图展示整体脉络:
4.3 分步拆解:规范请求(Canonical Request)
规范请求是 V4 签名的第一个核心产物,它将原始 HTTP 请求标准化为一个字符串。构造步骤如下:
- HTTP 方法:大写形式,如
PUT、GET。 - 规范 URI:对路径部分进行 URI 编码,保留斜杠。
- 规范查询字符串:对 Query 参数按字典序排序,进行 URI 编码,用
&连接。 - 规范头:将所有请求头名称转为小写,按字典序排序,格式为
key:value\n。 - 签名头列表:参与签名的头名称列表(小写、排序、分号分隔)。
- Payload 哈希:对请求体计算 SHA256 哈希(十六进制字符串)。
Go 代码示例:构造规范请求
func buildCanonicalRequest(method, uri, query string, headers map[string]string, payload []byte) string {
// 1. 排序并规范化请求头
var keys []string
for k := range headers {
keys = append(keys, strings.ToLower(k))
}
sort.Strings(keys)
var headerParts []string
for _, k := range keys {
headerParts = append(headerParts, k+":"+strings.TrimSpace(headers[k]))
}
canonicalHeaders := strings.Join(headerParts, "\n") + "\n"
// 2. 签名头列表
signedHeaders := strings.Join(keys, ";")
// 3. Payload 哈希
payloadHash := sha256Hex(payload)
// 4. 组合规范请求
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s",
method,
uri,
query,
canonicalHeaders,
signedHeaders,
payloadHash,
)
}
func sha256Hex(data []byte) string {
h := sha256.Sum256(data)
return hex.EncodeToString(h[:])
}
4.4 分步拆解:签名字符串与派生密钥
签名字符串(StringToSign)将规范请求与算法、时间戳、凭证范围绑定:
StringToSign = "AWS4-HMAC-SHA256" + "\n" +
yyyyMMddTHHmmssZ + "\n" + // ISO 8601 时间戳
YYYYMMDD + "/" + region + "/s3/aws4_request" + "\n" +
Hex(SHA256(CanonicalRequest))
派生密钥(Signing Key)通过四层 HMAC-SHA256 嵌套生成:
kSecret = "AWS4" + SecretAccessKey
kDate = HMAC-SHA256(kSecret, YYYYMMDD)
kRegion = HMAC-SHA256(kDate, region)
kService = HMAC-SHA256(kRegion, "s3")
kSigning = HMAC-SHA256(kService, "aws4_request")
常见错误:派生密钥时,日期必须与请求头
x-amz-date 中的日期一致,且使用 UTC 时间。如果日期不匹配,AWS 会返回 SignatureDoesNotMatch 错误。另外,区域名称必须小写(如 us-east-1),服务名称固定为 s3。
4.5 何时使用 V4 签名
以下场景必须或强烈建议使用 Signature V4:
- 所有新建 AWS S3 存储桶:自 2020 年 6 月起,新创建的存储桶仅支持 V4。
- 需要跨区域访问:V4 签名绑定区域,可防止请求被重放到其他区域。
- 需要 Payload 完整性校验:上传大文件或敏感数据时,V4 的 Content-SHA256 校验确保请求体未被篡改。
- 使用 AWS SDK 或 CLI:所有现代 AWS SDK(v2 及以上)默认使用 V4。
- 兼容 S3 的对象存储:MinIO、Ceph RGW、阿里云 OSS 等均支持 V4,且部分已弃用 V2。
- 安全合规要求严格的环境:金融、医疗、政府等场景强制使用 V4。
下面用一张图总结 V4 签名的完整数据流:
4.6 何时可以继续使用 V2(仅限遗留系统)
虽然 V2 已被弃用,但在以下受限场景中可能仍需处理 V2 签名:
- 维护旧版 SDK 或客户端:某些嵌入式设备或老旧系统无法升级到 V4。
- 兼容第三方网关:部分反向代理或缓存层仅实现了 V2 验证。
- 读取历史日志:分析过往请求时,需要理解 V2 签名的格式。
最佳实践:即使需要兼容 V2,也应在服务端同时支持 V4,并逐步将客户端迁移到 V4。在 Go 中,可以使用
aws-sdk-go-v2 的 s3.NewFromConfig() 自动处理 V4 签名,无需手动实现。