当前位置: 首页 > 知识库问答 >
问题:

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

曾洲
2023-03-14

我在一个网站上工作,用户应该能够上传视频文件到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 (I 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区分大小写的十六进制字符串。0xFF!=AWS眼中的0xff。他们希望签名都是小写的。

共有2个答案

羊舌承
2023-03-14

我建议您仅创建一个具有POST权限的pair auth令牌,并发送如下http请求:

require 'rest-client'

class S3Uploader
  def initialize
    @options = {
      aws_access_key_id: "ACCESS_KEY",
      aws_secret_access_key: "ACCESS_SECRET",
      bucket: "BUCKET",
      acl: "private",
      expiration: 3.hours.from_now.utc,
      max_file_size: 524288000
    }
  end

  def fields
    {
      :key => key,
      :acl => @options[:acl],
      :policy => policy,
      :signature => signature,
      "AWSAccessKeyId" => @options[:aws_access_key_id],
      :success_action_status => "201"
    }
  end

  def key
    @key ||= "temp/${filename}"
  end

  def url
    "http://#{@options[:bucket]}.s3.amazonaws.com/"
  end

  def policy
    Base64.encode64(policy_data.to_json).delete("\n")
  end

  def policy_data
    {
      expiration: @options[:expiration],
      conditions: [
        ["starts-with", "$key", ""],
        ["content-length-range", 0, @options[:max_file_size]],
        { bucket: @options[:bucket] },
        { acl: @options[:acl] },
        { success_action_status: "201" }
      ]
    }
  end

  def signature
    Base64.encode64(
      OpenSSL::HMAC.digest(
        OpenSSL::Digest.new("sha1"),
        @options[:aws_secret_access_key], policy
      )
    ).delete("\n")
  end
end

uploader = S3Uploader.new
puts uploader.fields
puts uploader.url

begin
  RestClient.post(uploader.url, uploader.fields.merge(file: File.new('51bb26652134e98eae931fbaa10dc3a1.jpeg'), :multipart => true))
rescue RestClient::ExceptionWithResponse => e
  puts e.response
end
赫连心思
2023-03-14

您正在使用签名版本4生成签名,但您正在构造表单,就像您在使用签名版本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-Signature。V4签名是十六进制的,所以这就是您在这里应该拥有的。V2签名是base64。

这里有一个完整的V4示例,它甚至为您提供了一个示例aws密钥和秘密、日期、区域、桶名称等。,您可以在代码中使用它来验证您确实得到了相同的响应。表单实际上不起作用,但重要的问题是您的代码是否可以生成相同的表单、策略和签名。

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

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

还要注意,我们计算的请求签名与您提供的签名不匹配。检查密钥和签名方法不会告诉您bucket策略或任何用户策略是否允许或拒绝请求。此错误不是权限错误。它将在权限检查之前抛出,仅基于签名的有效性,而不是AWS访问密钥id是否有权执行请求的操作,这是只有在签名验证后才测试的。

 类似资料:
  • 但是,没有关于如何完成这一点的例子。此外,如果没有设置凭据,即使在向S3请求上载之前,SDK也会出错 JS SDK文档中提到了这一点,因此似乎有可能:

  • 问题内容: 我刚接触selenium。我生成了我的第一个Javaselenium测试用例,它已成功编译。但是当我运行该测试时,我得到了以下RuntimeException 请告诉我如何解决此错误。 这是我要运行的Java文件。 我首先通过命令提示符启动了selenium服务器,然后通过另一个命令提示符执行了上述java文件。 第二个问题:我可以右键单击包含selenium的网页上的指定位置。 问题

  • 使用presigned URLs,你可以让浏览器直接上传一个文件到S3服务,而不需要暴露S3服务的认证信息给这个用户。下面就是使用minio-js实现的一个示例程序。 服务端代码 const MinIO = require('minio') var client = new MinIO.Client({ endPoint: 'play.min.io', port: 9000,

  • 我一直在努力让HTTPS访问我在Kubernetes的Elasticsearch集群。 我认为问题是库伯内特斯不喜欢我尝试使用的TLS证书,这就是为什么它没有将其一直传递到浏览器。 其他一切似乎都正常,因为当我接受Kubernetes入口控制器假证书时,请求会按预期进行。 在我尝试这样做的过程中,我设置了: 集群本身 一个nginx入口控制器 一个入口资源 以下是相关的yaml: > 入口资源 n

  • 我需要直接从浏览器上传一个文件到S3。开始时,我创建了一个脚本,该脚本正在工作,但为了授权,我需要将我的凭据accessKeyId和secretAccessKey放入其中,这是不安全的。 我发现我可以使用“授权签名”进行授权 这看起来不错,但我找不到在upload()方法中将此授权头放在请求的何处。 我的授权标头示例: 授权:AWS4-HMAC-SHA256凭证=/20151016//s3/AWS