Upload file to s3 on client side with rails, carri

2019-06-09 19:06发布

I keep getting a 403 when trying to upload from the client side. Is this due to not having conditions on the bucket? If I just specify the key - with no accesskey, signature, or policy - it will upload fine.

Bucket policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::example/*"
        }
    ]
}

CORS (open due to being local development)

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Signature generation

    ///whats returned - in controller

    string_to_sign
    set_s3_direct_post(photo)
    render :json => {
        :policy => @policy,
        :signature => sig,
        :key => Rails.application.secrets.aws_access_key_id,
        :success=>true,
        :store=> photo.photo.store_dir,
        :time => @time_policy,
        :time_date => @date_stamp,
        :form_data => @s3_direct_post
    }

 ------------------------------------------------------------------
    private

   def string_to_sign
     @time = Time.now.utc
     @time_policy = @time.strftime('%Y%m%dT000000Z')
     @date_stamp = @time.strftime('%Y%m%d')

     ret = {"expiration" => 1.day.from_now.utc.xmlschema,
            "conditions" =>  [
                {"bucket" => Rails.application.secrets.aws_bucket},
                {"x-amz-credential": "#{Rails.application.secrets.aws_access_key_id}/#{@date_stamp}/us-west-2/s3/aws4_request"},
                {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
                {"x-amz-date": @time_policy },
            ]
            }

      @policy = Base64.encode64(ret.to_json).gsub(/\n/,'').gsub(/\r/,'')

    end

    def getSignatureKey
        kDate = OpenSSL::HMAC.digest('sha256', ("AWS4" +  Rails.application.secrets.aws_secret_access_key), @date_stamp)
        kRegion = OpenSSL::HMAC.digest('sha256', kDate, 'us-west-2')
        kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
        kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
    end

    def sig
       sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), getSignatureKey, @policy).gsub(/\n|\r/, '')
    end

Client:

    var self=this;
    $(`#song-upload`).fileupload({
        url: `https://${self._backend.BUCKET}.s3.amazonaws.com`,
        dataType: 'json',
        add: function (e, data) {
          var data_add = data;
          $.ajax({
            url: `${self._backend.SERVER_URL}/api/photo/new`,
            data: {'authorization': `Bearer ${self._auth.isLoggedIn.getCookie('_auth')}`, post_type: 1, file_name:this.file_name},
            type: 'POST',
            success: function(data) {
              if(data.success){
                console.log(data);
                self.key = data.key;
                self.policy = data.policy;
                self.signature = data.signature;
                self.store_dir = data.store;
                self.upload_time = data.time;
                self.upload_date = data.time_date;
                data_add.submit();
               }
            }
          });
        },
        submit: function (e, data) {
          data.formData = {key:`${self.store_dir}/${self.file_name}`,AWSAccessKeyId: self.key, "Policy":self.policy, "x-amz-algorithm":"AWS4-HMAC-SHA256","Signature":self.signature,"x-amz-credential":`${self.key}/${self.upload_date}/us-west-2/s3/aws4_request`, "x-amz-date":self.upload_time};
        },
        progress: function (e, data) {
          var progress = Math.floor(((parseInt(data.loaded)*0.9)  / (parseInt(data.total))) * 100);
          $('#inner-progress').css({'transform':`translateX(${progress}%)`});
          $('#progress-text').text(progress);
        },
        done: function (e, data) {
            $('#inner-progress').css({'transform':`translateX(100%)`});
            $('#progress-text').text(100);
            if(e) console.log(e);
        }
    });

1条回答
smile是对你的礼貌
2楼-- · 2019-06-09 19:39

If someone has this, and is trying to do a javascript upload, try plugging in the values into the html file found here. Amazon will tell you the actual errors, instead of just a 403 response.

I was missing the ["starts-with", "$key", "uploads"] in my base64'd config.

Here's my end configurations:

Bucket Config:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Allow Get",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::example-development/*"
        },
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789:user/example"
            },
            "Action": "s3:*",
            "Resource": ["arn:aws:s3:::example-development/*","arn:aws:s3:::example-development"]
        }
    ]
}

Bucket

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Backend:

       string_to_sign
       set_s3_direct_post(song)
       render :json => {
                :policy => @policy,
                :signature => sig,
                :key => Rails.application.secrets.aws_access_key_id,
                :success=>true,
                :store=> song.song.store_dir,
                :time => @time_policy,
                :time_date => @date_stamp,
                :form_data => @s3_direct_post
       }

def string_to_sign

    @time = Time.now.utc
    @time_policy = @time.strftime('%Y%m%dT000000Z')
    @date_stamp = @time.strftime('%Y%m%d')

     ret = {"expiration" => 10.hours.from_now.utc.iso8601,
            "conditions" =>  [
                {"bucket" => 'waydope-development'},
                {"x-amz-credential": "#{Rails.application.secrets.aws_access_key_id}/#{@date_stamp}/us-west-2/s3/aws4_request"},
                {"x-amz-algorithm": "AWS4-HMAC-SHA256"},
                {"x-amz-date": @time_policy },
                ["starts-with", "$key", "uploads"]
            ]
            }
    @policy = Base64.encode64(ret.to_json).gsub(/\n|\r/, '')

end

def getSignatureKey
        kDate = OpenSSL::HMAC.digest('sha256', ("AWS4" +  Rails.application.secrets.aws_secret_access_key), @date_stamp)
        kRegion = OpenSSL::HMAC.digest('sha256', kDate, 'us-west-2')
        kService = OpenSSL::HMAC.digest('sha256', kRegion, 's3')
        kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
    end

def sig
        # sig = Base64.encode64(OpenSSL::HMAC.digest('sha256', getSignatureKey,  @policy)).gsub(/\n|\r/, '')
        sig = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), getSignatureKey, @policy).gsub(/\n|\r/, '')
end

Client:

var self=this;
    $(`#song-upload`).fileupload({
        url: `https://${self._backend.BUCKET}.s3.amazonaws.com`,
        dataType: 'multipart/form-data',
        add: function (e, data) {
          var data_add = data;
          $.ajax({
            url: `${self._backend.SERVER_URL}/api/music/new`,
            data: {'authorization': `Bearer ${self._auth.isLoggedIn.getCookie('_waydope')}`, post_type: 1, file_name:this.file_name},
            type: 'POST',
            success: function(data) {
              if(data.success){
                console.log(data);
                self.key = data.key;
                self.policy = data.policy;
                self.signature = data.signature;
                self.store_dir = data.store;
                self.upload_time = data.time;
                self.upload_date = data.time_date;
                data_add.submit();
               }
            }
          });
        },
        submit: function (e, data) {
          data.formData = {key:`${self.store_dir}/${self.file_name}`, "Policy":self.policy,"X-Amz-Signature":self.signature,"X-Amz-Credential":`${self.key}/${self.upload_date}/us-west-2/s3/aws4_request`,"X-Amz-Algorithm":"AWS4-HMAC-SHA256", "X-Amz-Date":self.upload_time, "acl": "public-read"};
        },
        progress: function (e, data) {
          var progress = Math.floor(((parseInt(data.loaded)*0.9)  / (parseInt(data.total))) * 100);
          $('#inner-progress').css({'transform':`translateX(${progress}%)`});
          $('#progress-text').text(progress);
        },
        done: function (e, data) {
            $('#inner-progress').css({'transform':`translateX(100%)`});
            $('#progress-text').text(100);
            if(e) console.log(e);
        }
    });
查看更多
登录 后发表回答