【问题标题】:AWS S3 presigned urls with boto3 - Signature mismatch带有 boto3 的 AWS S3 预签名 URL - 签名不匹配
【发布时间】:2018-08-05 17:34:22
【问题描述】:

我想为我的存储桶中的对象创建一个预签名的 url。我使用以下 python 代码:

    client = boto3.client(
    's3',
    aws_access_key_id=os.environ['AWS_ACCESS_KEY'],
    aws_secret_access_key=os.environ['AWS_SECRETS_KEY'],
    config=botocore.client.Config(signature_version='s3v4'),
    region_name='eu-central-1'
)
url = client.generate_presigned_url(
    ClientMethod='get_object',
    ExpiresIn=60,
    Params={
        'Bucket': MYBUCKET,
        'Key': MYKEY
    })

然后我将生成的 URL 发送到我的前端。在客户端上,我将使用生成的链接创建一个标签,并在其上使用 click() 方法。这在其他项目中运行良好,但在这里我只收到错误:

The request signature we calculated does not match the signature you provided. Check your key and signing method.

这很奇怪。用户应该拥有所有必要的权利。因为列出我存储桶中的所有文件都可以正常工作。

有人可以指出正确的方向,为什么这不起作用?

编辑

如果有帮助,我会在前端使用 next.js。

【问题讨论】:

    标签: python amazon-web-services amazon-s3 boto3


    【解决方案1】:

    遇到了完全相同的问题。研究了 AWS 文档并自己编写了(签名 v4)程序。以下是基于

    https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html

    并且完美运行。

    ENCODING = 'utf8'
    SEVEN_DAYS = 604800
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)
    
    
    def sign(key, msg):
        return hmac.new(key, msg.encode(ENCODING), hashlib.sha256).digest()
    
    
    def get_signature_key(key, dateStamp, regionName, serviceName):
        kDate = sign(('AWS4' + key).encode(ENCODING), dateStamp)
        kRegion = sign(kDate, regionName)
        kService = sign(kRegion, serviceName)
        kSigning = sign(kService, 'aws4_request')
        return kSigning
    
    
    def generate_presigned_s3_get(bucket, object_key, region, expires_in, access_key, secret_key):
        METHOD = 'GET'
        SERVICE = 's3'
        host = bucket + '.s3.' + region + '.amazonaws.com'
        endpoint = 'https://' + host
        t = datetime.datetime.utcnow()
        amz_date = t.strftime('%Y%m%dT%H%M%SZ')
        datestamp = t.strftime('%Y%m%d')
        canonical_uri = '/' + object_key
        canonical_headers = 'host:' + host + '\n'
        signed_headers = 'host'
        algorithm = 'AWS4-HMAC-SHA256'
        credential_scope = datestamp + '/' + region + '/' + SERVICE + '/' + 'aws4_request'
        canonical_querystring = '?X-Amz-Algorithm=AWS4-HMAC-SHA256'
        canonical_querystring += '&X-Amz-Credential=' + urllib.parse.quote_plus(access_key + '/' + credential_scope)
        canonical_querystring += '&X-Amz-Date=' + amz_date
        canonical_querystring += '&X-Amz-Expires=' + str(expires_in)
        canonical_querystring += '&X-Amz-SignedHeaders=' + signed_headers
        canonical_request = METHOD + '\n' + canonical_uri + '\n' + canonical_querystring[1:] + '\n' + canonical_headers + '\n' + signed_headers + '\nUNSIGNED-PAYLOAD'
        string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode(ENCODING)).hexdigest()
        signing_key = get_signature_key(secret_key, datestamp, region, SERVICE)
        signature = hmac.new(signing_key, (string_to_sign).encode("utf-8"), hashlib.sha256).hexdigest()
        canonical_querystring += '&X-Amz-Signature=' + signature
        url = endpoint + canonical_uri + canonical_querystring
        logger.info('presigned url: %s' % url)
        return url
    

    我也向 boto3 peeps 报告了这个问题: https://github.com/boto/boto3/issues/1644

    【讨论】:

    • 这太棒了。谢谢。
    • 你在这里使用什么编码?和 ENCODING 一样?
    • ENCODING = 'utf8'
    • 这很有魅力。这需要是公认的答案
    • 不起作用“我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法”
    【解决方案2】:

    问题出在boto3的版本上。

    我尝试了最新版本(boto3 1.7.14),但产生了上述错误。

    在 1.6.6 版中完全符合我的要求。

    【讨论】: