POST V4签名说明
POST签名是指使用POST方式上传文件时,要求每个请求都必须附带一个签名(Signature)。与POST V2签名方式不同的是,POST V4签名是通过访问密钥(Secret Access Key)、当前时间以及Region等信息对一系列请求参数(上传策略policy等)进行加密计算得到的。当请求发送时,会验证签名的合法性,只有签名验证成功的请求才会被接受,否则请求将被拒绝。
POST V4签名表单独有参数
POST V4签名除了POST上传对象中说明的公共表单元素外,会有一些独有的表单元素:
参数 | 参数类型 | 说明 |
---|---|---|
x-amz-algorithm | String | 指定签名的版本和算法,固定值为AWS4-HMAC-SHA256。 |
x-amz-credential | String | 指明派生密钥的参数集。格式:<AccessKeyId>/<date>/<region>/s3/aws4_request 。 |
x-amz-date | String | 请求的时间(遵循ISO 8601日期和时间标准)。格式:20241216T020211Z。 |
x-amz-signature | String | 签名认证信息。其值为通过HMAC-SHA256算法对Base64编码后的policy字符串进行加密计算,然后所得的二进制哈希值转换成十六进制的形式 |
Policy说明
Policy表单域是一种安全策略,若请求不包含Policy,则只能访问public-read-write的Bucket, 当Bucket为非public-read-write或者提供了AccessKeyId(或Signature)表单域时,必须提供Policy表单域 。 Policy由JSON格式来定义,可以通过多项参数来限制上传的相关操作,比如允许上传的Bucket、Key前缀等。
其中policy中有两个字段是必须的:
- expiration:指定policy的过期时间(ISO8601 GMT格式)。例如指定为2024-12-16T13:00:00.000Z,表示必须在2024年12月16日13点之前发起POST请求。
- conditions:指定POST请求表单域的合法值。
conditions表
参数 | 参数类型 | 必选 | 说明 | 匹配方式 | 示例 |
---|---|---|---|---|---|
bucket | String | 否 | 存储桶名称。 | bucket | {"bucket": "examplebucket"} |
key | String | 否 | 上传对象的名称。 | eq、starts-with、in和not-in | ["eq", "$key", "ExampleObject"] |
x-amz-algorithm | String | 是 | 指定签名的版本和算法,固定值为AWS4-HMAC-SHA256。 | x-amz-algorithm | {"x-amz-algorithm": "AWS4-HMAC-SHA256"} |
x-amz-credential | String | 是 | 指明派生密钥的参数集。格式:<AccessKeyId>/<date>/<region>/s3/aws4_request 。 |
x-amz-credential | {"x-amz-credential": "afwnu54***/20241216/us-east-1/s3/aws4_request"} |
x-amz-date | String | 是 | 请求的时间(遵循ISO 8601日期和时间标准)。格式:20241216T020211Z。 | x-amz-date | {"x-amz-date": "20241216T020211Z"} |
content-length-range | String | 否 | 上传对象时的最小以及最大允许的大小,单位是字节。 | content-length-range | ["content-length-range", 1, 10] |
success_action_status | String | 否 | 上传成功后的返回状态码。 | eq、starts-with、in和not-in | ["eq", "$success_action_status", "201"] |
content-type | String | 否 | 限制对象的文件类型。 | eq、starts-with、in和not-in | ["in", "$content-type", ["image/jpg", "image/png"]] |
x-amz-security-token | String | 否 | 安全令牌。仅在通过STS构造POST签名时需要该参数。可以通过调用STS服务的AssumeRole接口获取安全令牌。 | x-amz-security-token | {"x-amz-security-token": "ek+NvIdz***"} |
cache-control | String | 否 | 缓存控制。 | eq、starts-with、in和not-in | ["not-in", "$cache-control", ["no-cache"]] |
conditions匹配方式表
匹配方式 | 说明 |
---|---|
eq | 精确匹配。比如 ["eq", "$key", "ExampleObject"],则表单域中指定key的值必须为ExampleObject:{"key": "ExampleObject"}。 |
starts-with | 前缀匹配。比如 ["starts-with", "$key", "aaa/bbb"],则表单域中指定key的值必须以aaa/bbb开始:{"key": "aaa/bbb/ccc"}。 |
in | 包含匹配。比如 ["in", "$content-type", ["image/jpg", "image/png"]],则PostObject接口上传图片对象时,允许image/jpg或image/png格式的图片:{"content-type": "image/jpg"}。 |
not-in | 排除匹配。比如 ["not-in", "$content-type", ["image/jpg", "image/png"]],则PostObject接口上传图片对象时,不能允许image/jpg或image/png格式的图片对象。 |
示例
{
"expiration": "2024-12-16T13:00:00.000Z",
"conditions": [
{"bucket": "testbucket"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-credential": "afwnu54***/20241216/us-east-1/s3/aws4_request"},
{"x-amz-date": "20241216T020211Z"},
["content-length-range", 1, 10],
["eq", "$success_action_status", "201"],
["starts-with", "$key", "aaa/bbb/"],
["in", "$content-type", ["image/jpg", "image/png"]],
["not-in", "$cache-control", ["no-cache"]]
]
}
POST V4签名生成规则
Signature形式
Hex(HmacSHA256(kSigning,Base64(PolicyJSON)))
签名密钥kSigning的生成规则
kDate=HmacSHA256("AWS4"+{SecretAccessKey}, {dateStamp})
kRegion=HmacSHA256(kDate, {region})
kService=HmacSHA256(kRegion, {service})
kSigning=HmacSHA256(kService, "aws4_request")
签名步骤
- 第一步:创建utf-8编码的policy
- 第二步:构造签名字符串,在POST V4签名中,StringToSign是policy的Base64编码字符串
- 第三步:计算签名密钥,根据SecretAccessKey、dateStamp、region、service的值按照上述签名密钥的计算规则生成签名密钥kSigning
- 第四步:计算签名Signature,生成最终的签名时需要使用kSigning对StringToSign进行签名,该签名的计算方式为
Hex(HmacSHA256(kSigning,StringToSign))
,所得到的十六进制字符串即为Signature
生成POST V4签名参数代码示例
这里提供Python示例代码展示POST V4签名的计算过程:
import hmac
import hashlib
import base64
import datetime
import json
def hmac_sha256(key, data):
return hmac.new(key, data.encode('utf-8'), hashlib.sha256).digest()
def compute_signature(current_date, region, service, secret_key, policy_string):
k_secret = ('AWS4' + secret_key).encode('utf-8')
k_date = hmac_sha256(k_secret, current_date)
k_region = hmac_sha256(k_date, region)
k_service = hmac_sha256(k_region, service)
k_signing = hmac_sha256(k_service, 'aws4_request')
signature = hmac_sha256(k_signing, policy_string)
return bytes_to_hex(signature)
def bytes_to_hex(byte_data):
return ''.join(f'{byte:02x}' for byte in byte_data)
def get_date(date):
return date.strftime('%Y%m%d')
def get_timestamp(date):
return date.strftime('%Y%m%dT%H%M%SZ')
def main():
access_key = "访问密钥ID"
secret_key = "私有访问密钥"
bucket = "testbuck"
key = "testobj"
region = "us-east-1"
service = "s3"
algorithm = "AWS4-HMAC-SHA256"
date = datetime.datetime.utcnow()
request_date = get_date(date)
date_stamp = get_timestamp(date)
credential = f"{access_key}/{request_date}/{region}/{service}/aws4_request"
# 构建 policy
policy_json = {
"expiration": "2024-12-16T13:00:00.000Z",
"conditions": [
{"bucket": bucket},
{"x-amz-algorithm": algorithm},
{"x-amz-credential": credential},
{"x-amz-date": date_stamp},
["starts-with", "$key", key]
]
}
# 创建签名字符串
string_to_sign = base64.b64encode(json.dumps(policy_json).encode('utf-8')).decode('utf-8')
# 计算签名
signature = compute_signature(request_date, region, service, secret_key, string_to_sign)
print("policy:", string_to_sign)
print("x-amz-algorithm:", algorithm)
print("x-amz-credential:", credential)
print("x-amz-date:", date_stamp)
print("x-amz-signature:", signature)
if __name__ == '__main__':
main()
输出的结果:
policy: eyJleHBpcmF0aW9uIjogIjIwMjQtMTItMTZUMTM6MDA6MDAuMDAwWiIsICJjb25kaXRpb25zIjogW3siYnVja2V0IjogInRlc3RidWNrIn0sIHsieC1hbXotYWxnb3JpdGhtIjogIkFXUzQtSE1BQy1TSEEyNTYifSwgeyJ4LWFtei1jcmVkZW50aWFsIjogIlx1OGJiZlx1OTVlZVx1NWJjNlx1OTRhNUlELzIwMjQxMjE2L3VzLWVhc3QtMS9zMy9hd3M0X3JlcXVlc3QifSwgeyJ4LWFtei1kYXRlIjogIjIwMjQxMjE2VDAyMDIxMVoifSwgWyJzdGFydHMtd2l0aCIsICIka2V5IiwgInRlc3RvYmoiXV19
x-amz-algorithm: AWS4-HMAC-SHA256
x-amz-credential: 访问密钥ID/20241216/us-east-1/s3/aws4_request
x-amz-date: 20241216T020211Z
x-amz-signature: 65335e61c9c448fcc35283b12861f170f12f13ac03ef65037e44cb1f604048ca