使用HTTP POST的AWS S3浏览器上传给出无效签名

丹尼斯·雅各布森

我正在一个网站上,用户应该可以在该网站上将视频文件上传到AWS。为了避免不必要的流量,我希望用户直接上传到AWS(而不是通过API服务器)。为了不在JavaScript中公开我的密钥,我试图在API中生成一个签名。但是,它确实告诉我,当我尝试上传时,签名不匹配。

对于签名生成,我一直在使用http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html

在后端,我正在运行C#。

我使用生成签名

string policy = $@"{{""expiration"":""{expiration}"",""conditions"":[{{""bucket"":""dennisjakobsentestbucket""}},[""starts-with"",""$key"",""""],{{""acl"":""private""}},[""starts-with"",""$Content-Type"",""""],{{""x-amz-algorithm"":""AWS4-HMAC-SHA256""}}]}}";

产生以下内容

{"expiration":"2016-11-27T13:59:32Z","conditions":[{"bucket":"dennisjakobsentestbucket"},["starts-with","$key",""],{"acl":"private"},["starts-with","$Content-Type",""],{"x-amz-algorithm":"AWS4-HMAC-SHA256"}]}

基于http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html(我对策略进行base64编码)。作为一个起点,我试图使其保持非常简单。

为了生成签名,我使用在AWS站点上找到的代码。

static byte[] HmacSHA256(String data, byte[] key)
{
    String algorithm = "HmacSHA256";
    KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
    kha.Key = key;

    return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}

static byte[] GetSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
    byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
    byte[] kDate = HmacSHA256(dateStamp, kSecret);
    byte[] kRegion = HmacSHA256(regionName, kDate);
    byte[] kService = HmacSHA256(serviceName, kRegion);
    byte[] kSigning = HmacSHA256("aws4_request", kService);

    return kSigning;
}

我这样使用:

byte[] signingKey = GetSignatureKey(appSettings["aws:SecretKey"], dateString, appSettings["aws:Region"], "s3");
byte[] signature = HmacSHA256(encodedPolicy, signingKey);

其中dateString的格式为yyyymmdd

我使用以下方法从JavaScript发布信息

let xmlHttpRequest = new XMLHttpRequest();
let formData = new FormData();
formData.append("key", "<path-to-upload-location>");
formData.append("acl", signature.acl); // private
formData.append("Content-Type", "$Content-Type");
formData.append("AWSAccessKeyId", signature.accessKey);
formData.append("policy", signature.policy); //base64 of policy
formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request
formData.append("x-amz-date", signature.date);
formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");
formData.append("Signature", signature.signature);
formData.append("file", file);

xmlHttpRequest.open("post", "http://<bucketname>.s3-eu-west-1.amazonaws.com/");
xmlHttpRequest.send(formData);

按照AWS的规定,我到处都在使用UTF8。在他们的示例中,签名是十六进制格式,我也尝试过。无论我尝试什么,都会收到错误403

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

我在AWS上的政策有“ s3:Get *”,“ s3:Put *”

我是否缺少某些东西,或者它的工作方式完全不同于我的预期?

编辑:下面的答案是步骤之一。另一个是AWS区分大写和小写的十六进制字符串。在AWS看来0xFF!= 0xff 他们希望签名全部小写。

迈克尔-SQLbot

您正在使用Signature Version 4生成签名,但是您正在构建表单,就好像您在使用Signature Version 2一样。

formData.append("AWSAccessKeyId", signature.accessKey);

那是V2。根本不应该在这里。

formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request

这是V4。请注意此处和上方的冗余提交的AWS访问密钥ID。尽管示例中的大写字母如,这可能是正确的X-Amz-Credential

formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");

这也是正确的,除了可能需要是X-Amz-Algorithm(该示例似乎暗示大写被忽略了)。

formData.append("Signature", signature.signature);

这是不正确的。这应该是X-Amz-SignatureV4签名是十六进制的,因此您应该在这里拥有。V2签名是base64。

有一个完整的V4例如在这里,甚至提供了一个例子AWS密钥和密码,日期,区域,斗名称等,您可以与您的代码使用来验证你确实得到了同样的答复。该表单实际上无法正常工作,但重要的问题是您的代码是否可以生成相同的表单,策略和签名。

对于任何给定的请求,只有一个正确的签名。但是,对于任何给定的策略,可能有多个有效的JSON编码(由于JSON具有空格的灵活性),但是对于任何给定的JSON编码,该策略只有一种可能的有效base64编码。这意味着,如果您使用示例数据生成的代码和示例中生成的表单和签名完全相同,则您的代码将被证明可以正常工作;并且,如果您使用以下示例数据生成相同的表单和策略,则证明您的代码无效一个不同的签名-但是还有第三种可能性:如果您的代码生成了该策略的不同base64编码,则该测试实际上无法证明您的代码具有决定性,因为这必将使签名更改为不匹配,但仍然可能有效政策。

请注意,Signature V2仅支持在较旧的S3区域,而Signature V4受所有S3区域支持,因此,即使您可以通过使整个签名过程都使用V2来替代解决此问题,也不建议这样做。

另请注意,The request signature we calculated does not match the signature you provided. Check your key and signing method这不会告诉您有关存储桶策略或任何用户策略是允许还是拒绝该请求的任何信息。此错误不是权限错误。仅在签名有效性的基础上,才会在权限检查之前抛出该错误,而不是是否授权AWS Access Key ID执行所请求的操作,只有在验证签名之后才进行测试。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在Go中将预签名的POST上传到AWS S3?

使用HTTP POST将文件上传到S3存储桶

使用浏览器JavaScript SDK检查AWS S3上是否存在文件?

预签名的AWS S3 PUT URL无法使用jquery从客户端上传

使用POST的基于Amazon AWS S3浏览器的上传-

使用Postman通过HTTP POST上传图像

AWS S3直接上传返回无效签名(版本4签名)

使用Retrofit2将文件上传到AWS S3预签名URL

使用POST请求在AWS S3上基于浏览器的文件上传

使用AWS Lambda上传到AWS S3

AWS S3直接上传返回无效签名(版本4签名)C#

如何为带有std :: fstream中gz压缩文件的预签名url的AWS S3 HTTP上传设置内容长度?

AWS S3无法使用预签名URL检索文档:无效的日期(自纪元以来应为秒)

没有访问密钥的AWS S3 JavaScript浏览器上传

AWS仅使用ID和密钥将文件上传到S3服务器

Amazon S3上传图像-直接从浏览器使用angular js

使用JqueryFileUpload的Ruby on Rails Direct AWS S3上传

如何在Ruby中将预签名的POST文件上传到AWS S3?

Elixir中的浏览器POST命令的S3签名

使用 ClaudiaJS 和 AWS Lambda 发送 POST HTTP

通过 AWS-Java SDK 为浏览器签署 S3 上传请求

使用 cakephp 3 上传 AWS S3 文件

使用 API 网关、AWS LAMBDA 和 S3 从浏览器检索文件

AWS S3 HTTP POST - 重定向到带有 URL 参数的页面

Aws LAMBDA:HTTP POST 请求

使用预签名 URL 上传后,AWS S3 上的文件在文件中包含额外信息

为什么我使用预先签名的 AWS S3 url 上传的 pdf 没有正确的内容类型?

尝试使用 React 和 Express 将图像上传到 aws S3 时出现 HTTP 放置请求错误

使用 aws s3 对象作为使用 python-requests 的 HTTP POST 的输入