S3 Signature Does Not Match Error When Using A Pre Signed Request

Working with cutting-edge tech at RapidSpike our dev team often faces tricky, involved technical problems. These issues can be known to take a lot of head-scratching, fruitless research and no small amount of bad language.

Our solutions to these issues might be of use to future developers who encounter the same problem. So we’ve asked the team to share one or two here on the blog in the hope that it could prove useful. If you’re a developer and this post has helped you – let us know!

Jason is one of our Systems Developers – recently whilst working with S3 he had an issue using Pre Signed Requests, which was actually simple to resolve.


S3 PutObject with Pre Signed Request SignatureDoesNotMatch

AWS is great! The AWS SDK is great! S3 is great! AWS’s error messages are not great!

Recently I had to create a pre-signed request so users can upload files straight into an S3 bucket. Sounds simple and I have done something similar before, but things are often not simple.

So I made my pre signed url using the AWS SDK:

$cmd = $s3Client->getCommand('PutObject', array(
  	'Bucket' => $bucket,
   	'Key' => $key
));

$request = $s3Client->createPresignedRequest($cmd, '+20 minutes');
$uri = (string) $request->getUri();

This worked, the signed URL was created as expected.

From our AngularJS web app I added a simple upload form. Using the ng-file-upload package, I setup my promises to call the API, return the pre-signed url, then use this url to upload the file straight to an S3 bucket. Promises can cause enough issues, but surprisingly this time my promise chain worked first time! BOOM!

The code for the upload is below:

file.upload = Upload.upload({
   url: r,
   data: file,
   headers: {
       'Content-Type': file.type
   },
   method: POST,
   disableMultipart: true
})

The call to AWS was returning an error. An unhelpful error… SignatureDoesNotMatch. Looking at the AWS response a little bit more I could see a time stamp: 20170621T145509Z – an hour out.

I have had this problem before on other tasks (especially when using a VM), where the time can become out of sync, and usually restarting the VM fixes the issue. However this is never normally as much as an hour out. Restarting the VM did not work (or I would not be writing this blog post!)

Fixing the Timestamp

This lead me down a deep dark rabbit hole trying to fix the time stamp on my VM. After spending far too long trying to get my time in sync, changing the timezone on my VM and numerous restarts nothing was fixing this issue.

I realised if the time was at fault I would not be able to create the pre signed request in the first place, so something else was obviously causing the problem.

I found people who had similar problems on Stack Overflow (which again mentioned time been out of sync), I was about to give up. Then I noticed the word POST.

All I had to do was change the upload method in the angular app to use PUT instead of POST.

Almost there…

Now, using PUT method I was able to use the pre signed request url and upload files to S3 bucket.

However when downloading the files from S3 the files were wrapped in ------WebKitFormBoundaryrYYdQTQYGBSCTjH1 – now this is something I had not seen before.

Back down the rabbit hole we go…

After a while I found all I had to do was use the http method rather than upload. So my final JS code for the upload looks like this:

file.upload = Upload.http({
   url: r,
   data: file,
   headers: {
       'Content-Type': file.type
   },
   method: POST,
   disableMultipart: true
})

I understand why AWS only returns a generic error message when attempting to use a pre signed request, you can’t really tell the Average Joe exactly what is going wrong, however having clearer messages would make development with AWS a lot easier.

Hopefully someone in a similar situation can learn from the two issues I faced.