Amazon S3 POST api, and signing a policy with NodeJS

↘锁芯ラ 提交于 2020-01-20 14:23:50

问题


I'm trying to get an built that allows users to upload a file directly to my Amazon S3 bucket, from a NodeJS powered website. It seems the only tutorials out there, other than the actual amazon docs for this are all very out of date.

I've been following this tutorial, for the basic info, but again it's out dated. It doesn't have the method calls to crypto correct, as it tries to pass a raw JavaScript object to the update method, which throws an error because it's not a string or buffer.

I've also been looking at the source for the knox npm package. It doesn't have POST support built in - which I totally understand, because it's the browser doing the POST once it has the right fields. Knox does appear to have the right code to sign a policy, and I've tried to get my code working based on this... but again to no avail.

Here is what I've come up with, for code. It produces a base64 encoded policy, and it creates a signature... but it's the wrong signature according to Amazon, when I try to do a file upload.


var crypto = require("crypto");
var config = require("../../amazonConfig.json");

exports.createS3Policy = function(callback) {
  var date = new Date();

  var s3Policy = {
    "expiration": "2014-12-01T12:00:00.000Z",
    "conditions": [
      {"acl": "public-read"}, 
      ["content-length-range", 0, 2147483648],
      {"bucket": "signalleaf"}, 
      ["starts-with", "$Cache-Control", ""],
      ["starts-with", "$Content-Type", ""],
      ["starts-with", "$Content-Disposition", ""],
      ["starts-with", "$Content-Encoding", ""],
      ["starts-with", "$Expires", ""],
      ["starts-with", "$key", "/myfolder/"], 
      {"success_action_redirect": "http://example.com/uploadsuccess"},
    ]
  };

  var stringPolicy = JSON.stringify(s3Policy).toString("utf-8");
  var buffer = Buffer(stringPolicy, "utf-8");

  var encoded = buffer.toString("base64");
  var signature = crypto.createHmac("sha1", config.secretKey)
    .update(new Buffer(stringPolicy, "utf-8")).digest("base64");


  var s3Credentials = {
    s3PolicyBase64: encoded,
    s3Signature: signature
  };

  GLOBAL.s3creds = s3Credentials;

  callback(s3Credentials);
};

I'm obviously doing something wrong, here. But I have no idea what. Can anyone help identify what I'm doing wrong? Where my problem is? Does anyone have a working tutorial for how to generate a proper Amazon S3 Policy, with signature, from NodeJS v0.10.x, for a POST to the s3 REST api?


回答1:


Ok, I finally figured it out. After playing the random guessing game for a VERY long time, I thought to myself

"maybe i need to sign the base64 encoded policy" - me

and BAM that was it.

I also re-ordered the conditions to match how the form is posting, though I'm not sure this makes a difference.

var crypto = require("crypto");
var config = require("../../amazonConfig.json");

exports.createS3Policy = function(contentType, callback) {
  var date = new Date();

  var s3Policy = {
    "expiration": "2014-12-01T12:00:00.000Z", // hard coded for testing
    "conditions": [
      ["starts-with", "$key", "somefolder/"], 
      {"bucket": "my-bucket-name"}, 
      {"acl": "public-read"}, 
      ["starts-with", "$Content-Type", contentType],
      {"success_action_redirect": "http://example.com/uploadsuccess"},
    ]
  };

  // stringify and encode the policy
  var stringPolicy = JSON.stringify(s3Policy);
  var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64");

  // sign the base64 encoded policy
  var signature = crypto.createHmac("sha1", config.secretKey)
    .update(new Buffer(base64Policy, "utf-8")).digest("base64");

  // build the results object
  var s3Credentials = {
    s3Policy: base64Policy,
    s3Signature: signature
  };

  // send it back
  callback(s3Credentials);
};

Hopefully this will help others that run in to the same problem.




回答2:


I modified a bit previous example, because it didn't work for me: amazon returned an error about broken signature.

Here is how the signature should be created for Browser-Based Uploads Using POST (AWS Signature Version 4)

http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html

var CryptoJS = require("crypto-js");

var accessKeyID = "PUT YOUR DATA";
var secretAccessKey = "PUT YOUR DATA";

var bucket = "PUT YOUR BUCKET NAME";
var region = "eu-central-1"; // overwrite with your region
var folder = "users/"; // overwrite with your folder
var expiration = "2015-09-28T12:00:00.000Z"; // overwrite date
var date = "20150927"; // overwrite date
var serviceName = "s3";


function getSignatureKey(key, dateStamp, regionName, serviceName) {
   var kDate = CryptoJS.HmacSHA256(dateStamp, "AWS4" + key);
   var kRegion = CryptoJS.HmacSHA256(regionName, kDate);
   var kService = CryptoJS.HmacSHA256(serviceName, kRegion);
   var kSigning = CryptoJS.HmacSHA256("aws4_request", kService);

   return kSigning;
}

var s3Policy = {"expiration": expiration,
  "conditions": [
   {"bucket": bucket},
   ["starts-with", "$key", folder],
   {"acl": "public-read"},
   ["starts-with", "$Content-Type", "image/"],
   {"x-amz-meta-uuid": "14365123651274"},
   ["starts-with", "$x-amz-meta-tag", ""],
   {"x-amz-credential": accessKeyID + "/" + date + "/" + region + "/" + serviceName +"/aws4_request"},
   {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
   {"x-amz-date": date + "T000000Z" }
  ]
};

var base64Policy = new Buffer(JSON.stringify(s3Policy), "utf-8").toString("base64");
console.log('base64Policy:', base64Policy);

var signatureKey = getSignatureKey(secretAccessKey, date, region, serviceName);
var s3Signature = CryptoJS.HmacSHA256(base64Policy, signatureKey).toString(CryptoJS.enc.Hex);
console.log('s3Signature:', s3Signature);

Next generated base64Policy and s3Signature i used in the form for uploading. Example is here: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html

Very important is to check that you have the same fields and values in the html form and in your policy.




回答3:


I continued to have issues, so I worked through them and posted my solution here:

https://github.com/nikkwong/ng2-s3-uploader

In short, if you go with scabbiaza's answer in building the signature, make sure to build the form like so:

let formData = new FormData;
formData.append('acl', xAmzAcl);
formData.append('Content-Type', file.type);
formData.append('X-Amz-Date', xAmzDate);
formData.append('x-amz-server-side-encryption', xAmzServerSideEncryption);
formData.append('x-amz-meta-uuid', xAmzMetaUuid);
formData.append('X-Amz-Algorithm', xAmzAlgorithm);
formData.append('X-Amz-Credential', xAmzCredential);
formData.append('X-Amz-Signature', s3Signature);
formData.append('Policy', base64Policy);
formData.append('key', folder + '/' + file.name);
// File field must come last! 
formData.append('file', file);


来源:https://stackoverflow.com/questions/18476217/amazon-s3-post-api-and-signing-a-policy-with-nodejs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!