如何验证AWS SigV4签名?S3多区域访问点预签名URL校验
验证S3多区域访问点(MRAP)v4a预签名URL的签名真实性
我已经通过AWS为多区域访问点(MRAP)生成了用于获取S3对象的v4a签名预签名URL,生成代码如下:
from abc import ABC, abstractmethod from enum import Enum from typing import List, Optional, Tuple, TypedDict, Union, Dict import json import hmac import hashlib import urllib.parse from aiobotocore.client import AioBaseClient from botocore import awsrequest # type: ignore from botocore.credentials import Credentials # type: ignore from botocore.crt.auth import CrtS3SigV4AsymQueryAuth # type: ignore class S3Creds(TypedDict): """S3 creds.""" aws_access_key_id: str aws_secret_access_key: str aws_session_token: Optional[str] class MRAPSigner(AWSStorageResourceSigner): """AWS MRAP Signer.""" def __init__( self, endpoint: str, creds: S3Creds, ) -> None: """Initialize MRAPSigner.""" self._region = "*" self._endpoint = endpoint self._creds = Credentials( access_key=creds["aws_access_key_id"], secret_key=creds["aws_secret_access_key"], token=creds["aws_session_token"], ) self._s3_sig_v4_asym_query_auth = CrtS3SigV4AsymQueryAuth( self._creds, "s3", self._region, ) def generate_presigned_url( self, method: S3Method, item: str, file_type: Optional[str] = None, ) -> str: """Generate presigned url. Known Issue: - This function should be called not too late \ (like several minutes) after the creation of the signer. """ aws_request_config: Dict[str, Union[str, Dict]] = { "method": method.value, "url": self._endpoint + "/" + item, } if method == S3Method.PUT and file_type: headers: Dict[str, str] = {} headers["content-type"] = file_type aws_request_config["headers"] = headers asymmetric_query_request = awsrequest.AWSRequest(**aws_request_config) self._s3_sig_v4_asym_query_auth.add_auth(asymmetric_query_request) prepared_asymmetric_query = asymmetric_query_request.prepare() return prepared_asymmetric_query.url
我希望无需打开生成的URL,直接通过验证签名来确认其真实性,恳请提供相关帮助。我已查阅AWS官方文档中的《使用查询字符串进行身份验证》和《使用HTTP标头进行身份验证》内容。
验证步骤详解
v4a是S3多区域访问点专用的非对称签名变体,验证逻辑基于SigV4规范但有特殊适配,具体步骤如下:
1. 解析预签名URL的核心参数
先将目标URL解析为查询参数集合,提取以下关键字段:
X-Amz-Algorithm:确认值为AWS4-HMAC-SHA256(v4a的算法标识与标准SigV4一致)X-Amz-Credential:格式为AKIAXXX/*/s3/aws4_request,其中*是MRAP特有的通配符区域标识X-Amz-Date:签名生成的ISO8601格式时间戳X-Amz-SignedHeaders:签名覆盖的请求头列表(分号分隔)X-Amz-Signature:待验证的签名值- (可选)
X-Amz-Security-Token:临时凭证的会话令牌(如果生成URL时用了临时密钥)
2. 重构待签名的请求字符串
按照SigV4a规则,依次构建规范化请求和待签名字符串:
2.1 构建规范化请求
规范化请求格式固定为:
<HTTP方法> <规范化URI> <规范化查询字符串> <规范化请求头>\n <已签名头> <负载哈希值>
各部分说明:
- HTTP方法:必须与生成URL时使用的方法完全一致(如
GET/PUT) - 规范化URI:去掉MRAP端点域名后的路径部分,例如端点为
https://mrap-id.s3.dualstack.us-east-1.amazonaws.com、对象路径为docs/report.pdf,则URI为/docs/report.pdf - 规范化查询字符串:移除
X-Amz-Signature后,将剩余查询参数按字典序排序,键值对以key=value格式拼接,用&分隔(注意先对参数值做URL解码) - 规范化请求头:将
X-Amz-SignedHeaders中的头名转为小写,按字典序排序后格式化为key:value\n,最后追加一个空行 - 已签名头:直接使用
X-Amz-SignedHeaders的原始值(已排序的小写头名,分号分隔) - 负载哈希值:GET请求负载为空,固定哈希值为
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855;PUT请求需与生成URL时指定的content-type对应,若未指定负载则同样使用空负载哈希
2.2 构建待签名字符串
待签名字符串格式为:
AWS4-HMAC-SHA256 <X-Amz-Date> */s3/aws4_request <规范化请求的SHA256哈希值>
注意这里的区域部分固定为*,是MRAP签名的核心特征。
3. 计算签名并对比验证
使用生成URL时的AWS密钥,按照SigV4a的密钥派生规则计算签名:
- 生成基础密钥:
k_secret = "AWS4" + <AWS_SECRET_ACCESS_KEY> - 生成区域密钥:
k_region = hmac.new(k_secret.encode(), "*".encode(), hashlib.sha256).digest() - 生成服务密钥:
k_service = hmac.new(k_region, "s3".encode(), hashlib.sha256).digest() - 生成签名密钥:
k_signing = hmac.new(k_service, "aws4_request".encode(), hashlib.sha256).digest() - 计算最终签名:将步骤2.2生成的待签名字符串用
k_signing做HMAC-SHA256哈希,转换为十六进制字符串
将计算得到的签名与URL中的X-Amz-Signature值对比,若完全一致则说明签名真实有效。
4. 额外校验项
- 检查
X-Amz-Date与当前时间的差值,确认URL是否在有效期内(预签名URL默认有效期15分钟,可在生成时自定义) - 确认
X-Amz-Credential中的Access Key ID是拥有该MRAP对象访问权限的合法密钥 - (可选)若存在
X-Amz-Security-Token,可通过STS API验证临时凭证的有效性
内容的提问来源于stack exchange,提问作者Avanish Gupta
相关产品推荐
相关产品推荐

