我创建一个预签名 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?
在签名之前将自定义查询参数插入到 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。
这是 AWS 签名设计的一部分,此过程旨在实现更高级别的安全性。 AWS 从签名版本 2 更改为签名版本 4 的原因之一。
签名设计不知道哪些标头重要,哪些不重要。如果试图跟踪所有 AWS 服务,这将是一场噩梦。
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/
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