AWS S3带有其他查询参数的预先符号URL



我创建了一个预先签名的URL并恢复

之类的东西
https://s3.amazonaws.com/MyBucket/MyItem/
?X-Amz-Security-Token=TOKEN
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Date=20171206T014837Z
&X-Amz-SignedHeaders=host
&X-Amz-Expires=3600
&X-Amz-Credential=CREDENTIAL
&X-Amz-Signature=SIGNATURE

我现在可以curl这没问题。但是,如果我现在添加另一个查询参数,我将获得403,即。

https://s3.amazonaws.com/MyBucket/MyItem/
?X-Amz-Security-Token=TOKEN
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Date=20171206T014837Z
&X-Amz-SignedHeaders=host
&X-Amz-Expires=3600
&X-Amz-Credential=CREDENTIAL
&X-Amz-Signature=SIGNATURE
&Foo=123

怎么来?是否可以生成支持自定义查询的预先签名的URL?

在技术上似乎是可行的一种做到这一点的方法。

这是一种与AWS JavaScript SDK:

进行此回旋方式的示例
const AWS = require('aws-sdk');
var s3 = new AWS.S3({region: 'us-east-1', signatureVersion: 'v4'});
var req = s3.getObject({Bucket: 'mybucket', Key: 'mykey'});
req.on('build', () => { req.httpRequest.path += '?session=ABC123'; });
console.log(req.presign());

我已经使用以x-开头的自定义查询参数尝试了此操作。两者似乎都很好。我尝试了多个查询参数(?a=1&b=2),并且也有效。

自定义的预签名的URL正常工作(我可以使用它们来获取S3对象),并且查询参数将其用于CloudWatch日志中,因此可以用于相关目的。

请注意,如果要提供自定义到期时间,请按以下方式进行:

const Expires = 120;
const url = req.presign(Expires);

我不知道其他(非JavaScript)SDK,这些SDK使您可以将查询参数插入这样的URL构造过程中,因此以其他语言进行此操作可能是一个挑战。我建议使用小型JavaScript lambda函数(或API Gateway Plus Lambda函数),该功能只能创建并返回自定义的预签名URL。

自定义查询参数也是防篡改的。它们包括在URL的签名中,因此,如果您对它们篡改,则URL无效,产生403的禁止。

我使用此代码来生成您的预签名URL。结果是:

https://s3.amazonaws.com/MyBucket/MyItem
?Foo=123
&X-Amz-Algorithm=AWS4-HMAC-SHA256
&X-Amz-Credential=AKIA...27%2Fus-east-1%2Fs3%2Faws4_request
&X-Amz-Date=20180427T0012345Z
&X-Amz-Expires=3600
&X-Amz-Signature=e3...7b
&X-Amz-SignedHeaders=host

当然,如果AWS在封面下更改事物,但目前似乎有用,并且当然很有用。

归因:此发现的来源是AWS-SDK-JS/eskays/502。

如果更改标头或添加/减去,则必须辞职。

这是AWS签名设计的一部分,此过程是为更高级别的安全性而设计的。从签名版本2中更改为签名版本4的AWS原因之一。

签名设计不知道哪些标题很重要,哪些不知道。那将创造一场噩梦,试图跟踪所有AWS服务。

如果您正在寻找JavaScript SDK V3:

import { HttpRequest } from "@aws-sdk/protocol-http";
import { S3RequestPresigner } from "@aws-sdk/s3-request-presigner";
import { parseUrl } from "@aws-sdk/url-parser";
import { Sha256 } from "@aws-crypto/sha256-browser";
import { Hash } from "@aws-sdk/hash-node";
import { formatUrl } from "@aws-sdk/util-format-url";
// Make custom query in Record<string, string | Array<string> | null> format
const customQuery = {
  hello: "world",
};
const s3ObjectUrl = parseUrl(
  `https://${bucketName}.s3.${region}.amazonaws.com/${key}`
);
s3ObjectUrl.query = customQuery; //Insert custom query here
const presigner = new S3RequestPresigner({
  credentials,
  region,
  sha256: Hash.bind(null, "sha256"), // In Node.js
  //sha256: Sha256 // In browsers
});
// Create a GET request from S3 url.
const url = await presigner.presign(new HttpRequest(s3ObjectUrl));
console.log("PRESIGNED URL: ", formatUrl(url));

代码模板从:https://aws.amazon.com/blogs/developer/generate-presigned-url-modull-aws-aws-sdk-javascript/

我为Ruby SDK创建了此解决方案。这是一种黑客,但可以按预期的方式工作:

require 'aws-sdk-s3'
require 'active_support/core_ext/object/to_query.rb'
# Modified S3 pre signer class that can inject query params to the URL
#
# Usage example:
#
#    bucket_name = "bucket_name"
#    key = "path/to/file.json"
#    filename = "download_file_name.json"
#    duration = 3600
#
#    params = {
#      bucket: bucket_name,
#      key: key,
#      response_content_disposition: "attachment; filename=#{filename}",
#      expires_in: duration
#    }
#
#    signer = S3PreSignerWithQueryParams.new({'x-your-custom-field': "banana", 'x-some-other-field': 1234})
#    url = signer.presigned_url(:get_object, params)
#
#    puts "url = #{url}"
#
class S3PreSignerWithQueryParams < Aws::S3::Presigner
  def initialize(query_params = {}, options = {})
    @query_params = query_params
    super(options)
  end
  def build_signer(cfg)
    signer = super(cfg)
    my_params = @query_params.to_h.to_query()
    signer.define_singleton_method(:presign_url,
                                   lambda do |options|
                                     options[:url].query += "&" + my_params
                                     super(options)
                                   end)
    signer
  end
end

虽然未记录,您可以将参数作为参数添加到presigned_url的呼叫。

obj.presigned_url(:get, 
  expires_in: expires_in_sec, 
  response_content_disposition: "attachment"
)
https://bucket.s3.us-east-2.amazonaws.com/file.txt?response-content-disposition=attachment&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=PUBLICKEY%2F20220309%2Fus-east-2%2Fs3%2Faws4_request&X-Amz-Date=20220309T031958Z&X-Amz-Expires=43200&X-Amz-SignedHeaders=host&X-Amz-Signature=SIGNATUREVALUE

最新更新