带有其他查询参数的AWS S3预签名URL

问题描述 投票:0回答:6

我创建一个预签名 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?

amazon-web-services amazon-s3
6个回答
8
投票

在签名之前将自定义查询参数插入到 v4 预签名 URL 中似乎在技术上是可行的,但并非所有 AWS 开发工具包都公开了执行此操作的方法。

以下是使用 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- 开头和不带 X- 的自定义查询参数。两者似乎都工作正常。我尝试过使用多个查询参数(

?a=1&b=2

)并且也有效。

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

请注意,如果您想提供自定义过期时间,请按以下步骤操作:

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

我不知道其他(非 JavaScript)SDK 允许您像这样将查询参数插入到 URL 构建过程中,因此在其他语言中执行此操作可能是一个挑战。我建议使用小型 JavaScript Lambda 函数(或 API 网关加 Lambda 函数),它可以简单地创建并返回自定义的预签名 URL。

自定义查询参数也是防篡改的。它们包含在 URL 的签名中,因此,如果您篡改它们,URL 就会变得无效,并产生 403 Forbidden。

我使用此代码生成您的预签名 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/issues/502


5
投票
如果您更改其中一个标头或添加/删除,则必须放弃该 URL。

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

签名设计不知道哪些标头重要,哪些不重要。如果试图跟踪所有 AWS 服务,这将是一场噩梦。


1
投票
如果您正在寻找 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-modular-aws-sdk-javascript/


0
投票
我为 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
    

0
投票
虽然没有记录,但您可以将参数作为参数添加到对

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
    

0
投票
后续问题 - 是否可以添加一个参数,以便当用户访问预签名 URL 时。作为回报,我得到该用户使用的证书或“名称”。公司范围内基于用户证书,这就是我们授权用户可以或不能访问的内容的方式。

© www.soinside.com 2019 - 2024. All rights reserved.