我在一个网站上工作,用户应该能够上传视频文件到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。他们希望签名都是小写的。
我建议您仅创建一个具有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
您正在使用签名版本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
请帮忙。