我遇到了同样的问题,希望 boto 包能提供一种简单的方法来解决这个问题,但不幸的是它没有。
我也尝试使用boto在url上创建相同的签名库,但问题是时间戳(url中的X-Amz-Date)
要获得完全相同的签名,需要使用 url 中提供的时间戳来生成。
我掉进了兔子洞,试图“覆盖”日期时间,但这似乎是不可能的。
所以剩下的就是从头开始生成签名,就像你说你尝试过的那样。您链接的问题中的代码确实有效,但并不简单。
受该链接和boto3 源的启发,这是我创建的,并且似乎有效:
from urllib.parse import urlparse, parse_qs, urlencode, quote
import hashlib
import hmac
from django.conf import settings
def validate_s3_url(url, method='GET'):
"""
This check whether the signature in the given S3 url is valid,
considering the other parts of the url.
This requires that we have access to the (secret) access key
that was used to sign the request (the access key ID is
available in the url).
"""
parts = urlparse(url)
querydict = parse_qs(parts.query)
# get relevant query parameters
url_signature = querydict['X-Amz-Signature'][0]
credentials = querydict['X-Amz-Credential'][0]
algorithm = querydict['X-Amz-Algorithm'][0]
timestamp = querydict['X-Amz-Date'][0]
signed_headers = querydict['X-Amz-SignedHeaders'][0]
# if we have multiple access keys we could use access_key_id to get the right one
access_key_id, credential_scope = credentials.split("/", maxsplit=1)
host = parts.netloc
# important: in Python 3 this dict is sorted which is essential
canonical_querydict = {
'X-Amz-Algorithm': [algorithm],
'X-Amz-Credential': [credentials],
'X-Amz-Date': [timestamp],
'X-Amz-Expires': querydict['X-Amz-Expires'],
'X-Amz-SignedHeaders': [signed_headers],
}
# this is optional (to force download with specific name)
# if used, it's passed in as 'ResponseContentDisposition' Param when signing.
if 'response-content-disposition' in querydict:
canonical_querydict['response-content-disposition'] = querydict['response-content-disposition']
canonical_querystring = urlencode(canonical_querydict, doseq=True, quote_via=quote)
# build the request, hash it and build the string to sign
canonical_request = f"{method}\n{parts.path}\n{canonical_querystring}\nhost:{host}\n\n{signed_headers}\nUNSIGNED-PAYLOAD"
hashed_request = hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
string_to_sign = f"{algorithm}\n{timestamp}\n{credential_scope}\n{hashed_request}"
# generate signing key from credential scope.
signing_key = f"AWS4{settings.AWS_SECRET_ACCESS_KEY}".encode('utf-8')
for message in credential_scope.split("/"):
signing_key = hmac.new(signing_key, message.encode('utf-8'), hashlib.sha256).digest()
# sign the string with the key and check if it's the same as the one provided in the url
signature = hmac.new(signing_key, string_to_sign.encode('utf-8'), hashlib.sha256).hexdigest()
return url_signature == signature
这使用 django 设置来获取密钥,但实际上它可能来自任何地方。