浏览器向Amazon S3上传图片时出现Signature mismatch问题排查
问题:向Amazon S3通过表单上传图片时出现Signature mismatch错误
我正尝试按照AWS官方文档中的步骤,从浏览器直接向Amazon S3上传图片。为编码Policy,我编写了如下Python 2.7代码片段:
import hmac import hashlib import base64 def sign(key, msg): return hmac.new(key, msg, hashlib.sha256).digest() post_policy = """{ "expiration": "2018-12-30T12:00:00.000Z", "conditions": [ {"bucket": "<my_bucket>"}, ["starts-with", "$key", "user/user1/"], {"acl": "public-read"}, {"success_action_redirect": "http://my_bucket.s3.amazonaws.com/successful_upload.html"}, ["starts-with", "$Content-Type", "image/"], {"x-amz-meta-uuid": "14365123651274"}, {"x-amz-server-side-encryption": "AES256"}, ["starts-with", "$x-amz-meta-tag", ""], {"x-amz-credential": "<my-access-id>/20151229/us-east-2/s3/aws4_request"}, {"x-amz-algorithm": "AWS4-HMAC-SHA256"}, {"x-amz-date": "20181229T000000Z" } ] }""" post_policy_utf8 = unicode(post_policy, "utf-8") base64_encoded_policy = base64.b64encode(post_policy_utf8) secret_key = '<my_secret_key>' signed_policy = sign(("AWS4" + base64_encoded_policy), secret_key) encoded_signed_policy = base64.b64encode(signed_policy)
我的HTML表单如下:
<form action="http://<my_bucket>.s3.amazonaws.com/" method="post" enctype="multipart/form-data"> Key to upload: <input type="input" name="key" value="user/user1/${filename}" /><br /> <input type="hidden" name="acl" value="public-read" /> <input type="hidden" name="success_action_redirect" value="http://<my_bucket>.s3.amazonaws.com/successful_upload.html" /> Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br /> <input type="hidden" name="x-amz-meta-uuid" value="14365123651274" /> <input type="hidden" name="x-amz-server-side-encryption" value="AES256" /> <input type="text" name="X-Amz-Credential" value="AKIAJ5CZ54I742JS652A/20151229/us-east-2/s3/aws4_request" /> <input type="text" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" /> <input type="text" name="X-Amz-Date" value="20181229T000000Z" /> Tags for File: <input type="input" name="x-amz-meta-tag" value="" /><br /> <input type="hidden" name="Policy" value='eyAiZXhwaXJhdGlvbiI6ICIyMDE4LTEyLTMwVDEyOjAwOjAwLjAwMFoiLAogICJjb25kaXRpb25zIjogWwogICAgeyJidWNrZXQiOiAiZmlsZS51cGxvYWQuZnJvbS5icm93c2VyIn0sCiAgICBbInN0YXJ0cy13aXRoIiwgIiRrZXkiLCAidXNlci91c2VyMS8iXSwKICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sCiAgICB7InN1Y2Nlc3NfYWN0aW9uX3JlZGlyZWN0IjogImh0dHA6Ly9maWxlLnVwbG9hZC5mcm9tLmJyb3dzZXIuczMuYW1hem9uYXdzLmNvbS9zdWNjZXNzZnVsX3VwbG9hZC5odG1sIn0sCiAgICBbInN0YXJ0cy13aXRoIiwgIiRDb250ZW50LVR5cGUiLCAiaW1hZ2UvIl0sCiAgICB7IngtYW16LW1ldGEtdXVpZCI6ICIxNDM2NTEyMzY1MTI3NCJ9LAogICAgeyJ4LWFtei1zZXJ2ZXItc2lkZS1lbmNyeXB0aW9uIjogIkFFUzI1NiJ9LAogICAgWyJzdGFydHMtd2l0aCIsICIkeC1hbXotbWV0YS10YWciLCAiIl0sCiAgICAKICAgIHsieC1hbXotY3JlZGVudGlhbCI6ICJBS0lBSjVDWjU0STc0MkpTNjUyQS8yMDE1MTIyOS91cy1lYXN0LTIvczMvYXdzNF9yZXF1ZXN0In0sCiAgICB7IngtYW16LWFsZ29yaXRobSI6ICJBV1M0LUhNQUMtU0hBMjU2In0sCiAgICB7IngtYW16LWRhdGUiOiAiMjAxODEyMjlUMDAwMDAwWiIgfQogIF0KfQ==' /> <input type="hidden" name="X-Amz-Signature" value="ZG0xBgY5y1Fin0D7kKAO9tuqGntG1Ptw4GDaiLvfvGU=" /> File: <input type="file" name="file" /> <br /> <!-- The elements after this will be ignored --> <input type="submit" name="submit" value="Upload to Amazon S3" /> </form>
Policy的Base64值和签名均由上述Python 2.7代码生成,但提交表单时AWS提示Signature mismatch。请问问题出在哪里,该如何修正?
问题分析与解决方案
你遇到的签名不匹配问题主要来自三个核心错误,下面逐一说明并给出修正方案:
1. SigV4签名密钥生成逻辑错误
AWS SigV4的签名流程不是直接用AWS4 + base64编码的policy去和密钥签名,而是需要分层推导签名密钥:
- 第一步:用日期(格式
YYYYMMDD)和AWS4<你的秘钥>生成第一个HMAC哈希 - 第二步:用区域(比如
us-east-2)和第一步的结果生成第二个哈希 - 第三步:用服务名称
s3和第二步的结果生成第三个哈希 - 第四步:用
aws4_request和第三步的结果生成最终的签名密钥 - 最后用这个签名密钥去签名base64编码后的policy
2. Policy中的日期与Credential日期不一致
你的Policy里x-amz-date是20181229T000000Z,但x-amz-credential里的日期是20151229,这两个日期必须完全一致(格式YYYYMMDD部分要匹配),否则AWS会判定签名无效。
3. Python编码处理错误
在Python 2.7中,unicode(post_policy, "utf-8")的写法是错误的:如果post_policy本身是字符串(str类型),应该用post_policy.encode('utf-8')来转成UTF-8编码的字节流,否则会导致policy的编码不正确,进而签名不匹配。
修正后的Python 2.7代码
import hmac import hashlib import base64 def sign(key, msg): return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest() def get_signature_key(secret_key, date_stamp, region_name, service_name): # 按照SigV4步骤生成签名密钥 k_date = sign(('AWS4' + secret_key).encode('utf-8'), date_stamp) k_region = sign(k_date, region_name) k_service = sign(k_region, service_name) k_signing = sign(k_service, 'aws4_request') return k_signing # 统一日期(确保和policy里的日期一致) date_stamp = '20181229' amz_date = '20181229T000000Z' access_id = '<my-access-id>' bucket_name = '<my_bucket>' secret_key = '<my_secret_key>' region = 'us-east-2' post_policy = """{{ "expiration": "2018-12-30T12:00:00.000Z", "conditions": [ {{"bucket": "{bucket}"}}, ["starts-with", "$key", "user/user1/"], {{"acl": "public-read"}}, {{"success_action_redirect": "http://{bucket}.s3.amazonaws.com/successful_upload.html"}}, ["starts-with", "$Content-Type", "image/"], {{"x-amz-meta-uuid": "14365123651274"}}, {{"x-amz-server-side-encryption": "AES256"}}, ["starts-with", "$x-amz-meta-tag", ""], {{"x-amz-credential": "{access_id}/{date_stamp}/{region}/s3/aws4_request"}}, {{"x-amz-algorithm": "AWS4-HMAC-SHA256"}}, {{"x-amz-date": "{amz_date}"}} ] }}""".format(bucket=bucket_name, access_id=access_id, date_stamp=date_stamp, region=region, amz_date=amz_date) # 正确处理编码:转成UTF-8字节流 post_policy_utf8 = post_policy.encode('utf-8') base64_encoded_policy = base64.b64encode(post_policy_utf8) # 生成签名密钥并签名policy signing_key = get_signature_key(secret_key, date_stamp, region, 's3') signed_policy = hmac.new(signing_key, base64_encoded_policy, hashlib.sha256).digest() encoded_signed_policy = base64.b64encode(signed_policy) # 打印结果,用于填充到HTML表单 print("Policy Base64:", base64_encoded_policy) print("Signature:", encoded_signed_policy)
修正后的HTML表单要点
- 确保
X-Amz-Credential中的日期部分是20181229,和X-Amz-Date的日期一致 - 替换
Policy和X-Amz-Signature的值为修正后代码输出的结果 - 确认表单中的所有隐藏字段值和Policy里的conditions完全匹配(比如
acl、x-amz-meta-uuid等)
内容的提问来源于stack exchange,提问作者Sujay DSa
相关产品推荐
相关产品推荐

