Search code examples
ruby-on-railsamazon-web-servicesamazon-s3corsamazon-cloudfront

CORS with Amazon S3 and Cloudfront


I have a Rails App hosted on Heroku which is using CloudFront with assets hosted on S3.

It displays assets prefectly(although it took some wrestling).

My setup for Cloudfront:

Forward Headers: Whitelist
Whitelist Headers: Origin
Forward Query Strings: No

CORS setup for the S3 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>HEAD</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

The JS that calls the image(coffee)

@mousemove (e) ->
  ctx = $("<canvas>").attr(
    width: @width
    height: @height
  )[0].getContext("2d")

  ctx.drawImage(this, 0, 0, @width, @height)

  # This is because firefox doesn't support offset[X|Y]
  if(e.offsetX == undefined)
    xpos = e.pageX - $(this).offset().left;
    ypos = e.pageY - $(this).offset().top;
  else
    xpos = e.offsetX;
    ypos = e.offsetY;

  e.rgb = ctx.getImageData(xpos, ypos, 1, 1).data
  return f.call this, e

This errors with: Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.

Curl request:

curl -i -H "Origin: http://example.com/" http://randomness.cloudfront.net/franchisees/logos/000/000/006/icon/Screen_Shot_2014-08-06_at_15.21.14.png?1407334881

  Response:
  HTTP/1.1 200 OK
  Content-Type: image/png
  Content-Length: 6888
  Connection: keep-alive
  Date: Wed, 06 Aug 2014 16:15:54 GMT
  Access-Control-Allow-Origin: *
  Access-Control-Allow-Methods: GET, HEAD
  Last-Modified: Wed, 06 Aug 2014 14:21:23 GMT
  ETag: "6f266467cf0a570526869bcf280da412"
  Accept-Ranges: bytes
  Server: AmazonS3
  Age: 21
  Vary: Origin
  X-Cache: Hit from cloudfront
  Via: 1.1 16d4ae36524b457e558b982004526450.cloudfront.net (CloudFront)
  X-Amz-Cf-Id: 5Vm_eCn_lTOHEMzPEcmtCGYCUdOZ2r_9R4W6mEPTOPFUIJe0ilMP7g==

 *snip*

Its not a caching issue I've been using fresh images every time, although this request was on a cached version.

I know there are work arounds, but I would really like it to work this way.

What am I missing here?


Solution

  • The problem is that by default the browser won't check these cors headers - all images pulled in are dirty. For your cors headers to do anything you need to set the crossorigin attribute on the img, so that you get a cors enabled image.

    <img src="blah" crossorigin="anonymous" />
    

    The crossorigin attribute can take the values anonymous or use-credentials. This is analogous to the withCredentials option when making cross domain ajax requests: by default cookies, http auth etc won't be sent. If you're creating the image in javascript then the corresponding property is crossOrigin.

    According to the MDN documentation Chrome, Firefox and IE 11 support this, but released versions of Opera or Safari don't