Last Updated: August 20, 2016
·
8.692K
· lukasz-madon

Uploading to s3 returns SignatureDoesNotMatch

I've been implementing direct upload to S3 for my heroku app. For some time to time I was getting 403 response with SignatureDoesNotMatch error. After investigation I found the problem. AWS S3 doesn't like + in url. Instead of quote_plus I used quote. Fixed code:

@app.route("/sign-s3/")
def sign_s3():
    AWS_ACCESS_KEY = app.config["AWS_ACCESS_KEY_ID"]
    AWS_SECRET_KEY = app.config["AWS_SECRET_ACCESS_KEY"]
    S3_BUCKET = app.config["S3_BUCKET"]

    object_name = quote(request.args.get("s3_object_name").encode('ascii', 'ignore'))  # ignoring unicode for now (hmac issue)
    mime_type = request.args.get("s3_object_type")

    expires = int(time.time()) + 60  # 60 sec for starting request should be enough 
    amz_headers = "x-amz-acl:public-read"  

    put_request = "PUT\n\n%s\n%d\n%s\n/%s/%s" % (mime_type, expires, amz_headers, S3_BUCKET, object_name)
    signature = base64.encodestring(hmac.new(AWS_SECRET_KEY, put_request, sha1).digest())
    signature = quote(signature.strip()).replace("/", "%2F")

    app.logger.info("signing for %s with signature %s", put_request, signature)
    url = "https://%s.s3.amazonaws.com/%s" % (S3_BUCKET, object_name)

    return jsonify({
        "signed_request": "%s?AWSAccessKeyId=%s&Expires=%d&Signature=%s" % (url, AWS_ACCESS_KEY, expires, signature),
         "url": url
      })

I also was getting net::ERR_CONNECTION_RESET. I found the bug in s3upload.js. When the singing callback runs it encodes the url in the wrong format with decodeURIComponent. Here is the pull request https://github.com/tadruj/s3upload-coffee-javascript/pull/8

2 Responses
Add your response

Good catch! Here's the solution in node.js in case it will help anyone:

signs3: function(req, res){
var AWS
ACCESSKEY = <access key from config>;
var AWS
SECRETKEY = <secret key from config>;
var S3
BUCKET = <bucket name>;

var object_name = uuid.v4(); //I'm using uuids as my object_names
var mime_type = req.query.s3_object_type;

var now = new Date();
var expires = Math.ceil((now.getTime() + 60000)/1000); // 60 seconds from now
var amz_headers = "x-amz-acl:public-read";

var put_request = "PUT\n\n"+mime_type+"\n"+expires+"\n"+amz_headers+"\n/"+S3_BUCKET+"/"+object_name;

var signature = crypto.createHmac('sha1', AWS_SECRET_KEY).update(put_request).digest('base64');

signature = signature.replace('+','%2B')
                                 .replace('/','%2F')
                                 .replace('=','%3D');
signature = encodeURIComponent(signature.trim());

var url = 'https://'+S3_BUCKET+'.s3.amazonaws.com/'+object_name;

var credentials = {
    signed_request: url+"?AWSAccessKeyId="+AWS_ACCESS_KEY+"&Expires="+expires+"&Signature="+signature,
    url: url
};
res.write(JSON.stringify(credentials));
res.end();

},

over 1 year ago ·

In my case this error was caused by extra space after colon in "x-amz-server-side-encryption: AES256". After I removed the space, the request started working.

over 1 year ago ·