15

I have a site running at pixie.strd6.com and images hosted through Amazon S3 with a CNAME for images.pixie.strd6.com.

I would like to be able to draw these images to an HTML5 canvas and call the getImageData method but it throws Error: SECURITY_ERR: DOM Exception 18

I have tried setting window.domain = "pixie.strd6.com", but that has no effect.

Additionally, $.get("http://dev.pixie.strd6.com/sprites/8516/thumb.png?1293830982", function(data) {console.log(data)}) also throws an error: XMLHttpRequest cannot load http://dev.pixie.strd6.com/sprites/8516/thumb.png?1293830982. Origin http://pixie.strd6.com is not allowed by Access-Control-Allow-Origin.

Ideally HTML5 canvas wouldn't block calling getImageData from subdomains. I've looked into setting an Access-Control-Allow-Origin header in S3, but haven't succeeded.

Any help or workarounds are greatly appreciated.

EricLaw
  • 56,563
  • 7
  • 151
  • 196
Daniel X Moore
  • 14,637
  • 17
  • 80
  • 92
  • 36
    This same origin policy is the dumbest thing ever. If I'm a malicious piece of JavaScript and I want to load malicious data I'll just include an arbitrary script tag in the page, not read "s3kri7 c0mm4nd5" from image data. The only people who want to read image data are client side devs. As for stealing "top secret image data" from a vpn, if your site has already been xss'd then keylogging will be much more devastating. All this "protection" serves to do is aggravate legitimate developers trying to get JavaScript to do the simplest of tasks. – Daniel X Moore Jan 12 '11 at 23:32
  • 8
    The SOP is protecting against a legitimate attack vector here. Suppose you have a private photo album on a photo-sharing site (or check images stored in your online banking): without dirty canvas protection, *any page on the Web* you visit would have the power to grab those images if they knew the URL and you were logged in, because requests sent from `` tags **use your cookies**. The problem here isn't compromised XSS'd sites; the problem is that *any page on the Web* could fetch and read images on a canvas using your authentication cookies. – apsillers Dec 19 '12 at 16:25
  • 2
    **tl;dr:** As it stands now, any cross-domain site can *display* your auth-required images (private photos, check images, etc.) in an `` tag, but, thanks to the SOP, they can't *read* the contents of those images in a canvas in order to, e.g., save them to a server. – apsillers Dec 19 '12 at 16:27
  • 3
    I have come around to agree that this does protect against some legitimate attack vectors. Now with CORS support becoming common it is possible to correctly allow access to resources hosted at external domains. It is still a pain for your average developer because the cost/benefit of the extra security for cat photos is low, but it is important that the internet remain secure for banks which inexplicably display sensitive information in images. – Daniel X Moore Dec 19 '12 at 23:24
  • i wonder what would there be if i do like `document.imd={};` then do `document.imd[elementId] = document.getElementById(elementId).getContext("2d").getImageData(0,0,img.width, img.height);` and only after that would run `document.getElementById(elementId).getContext("2d").drawImage(img,0,0)`. What would browser do then, if i get reference to image data BEFORE it was tainted? – Arioch 'The May 08 '13 at 14:23

9 Answers9

6

Amazon recently announced CORS support

We're delighted to announce support for Cross-Origin Resource Sharing (CORS) in Amazon S3. You can now easily build web applications that use JavaScript and HTML5 to interact with resources in Amazon S3, enabling you to implement HTML5 drag and drop uploads to Amazon S3, show upload progress, or update content. Until now, you needed to run a custom proxy server between your web application and Amazon S3 to support these capabilities.

How to enable CORS

To configure your bucket to allow cross-origin requests, you create a CORS configuration, an XML document with rules that identify the origins that you will allow to access your bucket, the operations (HTTP methods) will support for each origin, and other operation-specific information. You can add up to 100 rules to the configuration. You add the XML document as the cors subresource to the bucket.

Daniel X Moore
  • 14,637
  • 17
  • 80
  • 92
4

One possible solution is to use nginx to act as a proxy. Here is how to configure urls going to http://pixie.strd6.com/s3/ to pass on through to S3, but the browser can still believe that it is non-cross domain.

location /s3/ {
  proxy_pass http://images.pixie.strd6.com/;
}
Daniel X Moore
  • 14,637
  • 17
  • 80
  • 92
3

If you are using PHP, you can do something like:

    function fileExists($path){
        return (@fopen($path,"r")==true);
    }
    $ext = explode('.','https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg');
    if(fileExists('https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg')){
        $contents = file_get_contents('https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg');
        header('Content-type: image/'.end($ext));
        echo $contents;
    }

And access the image by using that php file, like if the file is called generateImage.php you can do <img src="http://GENERATEPHPLOCATION/generateImage.php"/> and the external image url can be a get parameter for the file

2

For people who do not use S3 can try to build a image proxy that encode the image file and wrap it into a JSON object.

Then you can use JSONP which supports cross domain to fetch the JSON object and assign the image data to img.src .

I wrote a sample code of the image proxy server with Google App Engine. https://github.com/flyakite/gae-image-proxy

The JSON object returns in the format like this

{ 
  'height': 50, 
  'width' : 50, 
  'data'  : 'data:image/jpeg;base64,QWRarjgk4546asd...QWAsdf'
} 

The 'data' is the image data in base64 format. Assign it to a image.

img.src = result.data;

The image is now "clean" for your canvas.

Shih-Wen Su
  • 2,589
  • 24
  • 21
2

To edit your S3 bucket permissions:

1) Sign in to the AWS Management Console and open the Amazon S3 console at https://console.aws.amazon.com/s3/

2) In the Buckets list, open the bucket whose properties you want to view and click "add CORS configuration"

amazon-screen-shot

3) Write the rules you are willing to add in between the tags <CORSConfiguration>

<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

You can learn more about rules at: http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html

4) Specify crossorigin='anonymous' on the image you'll use in your canvas

Flavio Wuensche
  • 9,460
  • 1
  • 57
  • 54
2

Recently, I came across $.getImageData, by Max Novakovic. The page includes a couple of neat demos of fetching and operating on Flickr photos, along with some code examples.

It allows you to fetch an image in JavaScript-manipulable form from an arbitrary site. It works by appending a script to the page. The script then requests the image from a Google App Engine server. The server fetches the requested image and relays it converted to base64 to the script. When the script receives the base64, it passes the data to a callback, which can then draw it onto a canvas and begin messing with it.

George
  • 3,027
  • 1
  • 24
  • 17
2

In the past Amazon S3 didn't allow you to modify or add the access-control-allow-origin and access-control-allow-credentials HTTP headers so it may have been better to switch to a different service like Rackspace Cloud Files or some other service that does.

Add or modify the HTTP headers like this:

access-control-allow-origin: [your site]
access-control-allow-credentials: true

See http://www.w3.org/TR/cors/#use-cases for more information.

Using a service that allows you to modify the HTTP headers entirely solves the same origin problem.

Daniel X Moore
  • 14,637
  • 17
  • 80
  • 92
1

This behavior is by-design. Per the HTML5 spec, as soon as you draw a cross-origin image to a canvas, it is dirty and you can no longer read the pixels. Origin-matching compares the scheme, fully-qualified host, and in non-IE browsers, the port.

EricLaw
  • 56,563
  • 7
  • 151
  • 196
  • 4
    Yes, server code to proxy the images will work, but it is sad that we have to rely on something that would not be needed. They should at least implement cross domain policies via an xml file on the target server, as was done with flash. – Omiod Jan 12 '11 at 21:45
1

Just bumped into the same problem. I found out about CORS that might be helpful.

http://html5-demos.appspot.com/static/html5-whats-new/template/index.html#14

It didn't work for me since I'm trying to manipulate an image from Flickr. So, I'm still looking for the solution.

Gustavo
  • 235
  • 4
  • 11