SecurityException 1000, even though using same domain - javascript

I'm facing a troublesome Javascript/Firefox problem.
Relevant code is listed below.
What happens basically is the following:
1. document.ready fires and initiates an AJAX request (to document.domain:8484/getTrack.php or whatever)
2. AJAX response is received. This response contains the url (same domain) of the location of the image. So, sourceImage.onload is set, then sourceImage.src is set
3. sourceImage.onload fires. The idea is now to keep a resized image in memory that perfectly fits the canvas it's going to be drawn on. I want to keep this resized image in memory because I'm going to write (parts of) it to my canvas a lot of times, and resizing every time should be a lot slower.
var SourceImage = new Image();
var preparedImageData;
sourceImage.onload = function() {
var canvas = document.createElement('canvas');
canvas.width = 100; canvas.height = 100;
var ctx = canvas.getContext("2d");
// resize image
ctx.drawImage(sourceImage, 0, 0, sourceImage.width, sourceImage.height, 0, 0, canvas.width, canvas.height);
// save as imagedata
try {
try {
preparedImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
catch (e) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
preparedImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
}
catch (e) {
throw new Error("unable to access image data: " + e)
}
}
The first getImageData call throws and the enablePrivilege call also throws inmediately. The errror text is "A script from "http://127.0.0.1" was denied UniversalBrowserRead privileges.". I've checked and it appears these messages should only appear when trying to access getImageData on an image from another domain, which isn't the case though (right?). have no strict security policy in place (everything default), Firefox 4.0. Same code works fine on Chrome.

By 'same origin' ref the Same Origin Policy, the protocol, hostname AND port needs to be identical. I'm guessing you are using different ports here?
What I think happens is that your call to netscape.security.PrivilegeManager.enablePrivilege fails due to the script not being signed - have you tried removing this code?

The context.getImageData and the PrivilegeManager.enablePrivilege calls fail as soon as I set document.domain = document.domain which is done for cooperation with iframes hosted on a different subdomain. As a workaround I proxied domain.tld/subdomain/ to subdomain.domain.tld/ and obtained the desired result.

Related

A strange problem, there are many solutions in Stackoverflow, but no one can work(Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted)

I am doing a verification code to automatically fill in the plugin, but I have encountered an error.
Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
Error Image:
https://img-ask.csdn.net/upload/201902/03/1549128446_976961.png
The corresponding code is:
Code Image
https://img-ask.csdn.net/upload/201902/03/1549128473_324557.png
function getBase64Image(im)
{
im.crossOrigin="";
var canvas = document.createElement("canvas");
canvas.width = im.width;
canvas.height = im.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(im, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace("data:image/png;base64,", "");
}
I found a lot of solutions in Google, mostly telling me that this is a cross-domain error.
solutions
I can add code to be
img.crossOrigin="Anonymous";
Resolve cross-domain issues, but still give an error after trying
Next, I found a solution to this type of problem on stackoverflow.
Got it
img.crossOrigin="";
Still added after the addition
This solution is feasible on other pages, that is, this page cannot be killed or killed.
Test:
Test URL::Click here to jump to the Alipay login page
1.Enter a function in the Console
Such as:
function getBase64Image(im)
{
im.crossOrigin="";
var canvas = document.createElement("canvas");
canvas.width = im.width;
canvas.height = im.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(im, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace("data:image/png;base64,", "");
}
2.Next
getBase64Image(document.getElementById("J-checkcode-img"))
I don't know much about JavaScript development.
I haven't found a suitable solution on the Internet for a long time.
I don't know how to solve it. I hope you can help, thank you!
In your research it seems you read on the crossOrigin attribute, which indeed is required for being able to access cross-origin resources' data.
However, it seems you didn't clearly got how it works.
This attribute is a flag for the browser to know if they must request the resource fetched by the Element with CORS headers, so that the server can send them the response with the proper Allow-Origin headers (or with a denial).
But this attribute doesn't call anything by itself, it is juts a flag and is only visited when the Element's resource is being fetched.
This means that for it to have any effect, you must set it before setting the Element's src.
So when in your code you do im.crossOrigin = ""; since you don't set the src again after this setting, the resource fetched doesn't have the proper Allow-Origin headers, and the resource is marked as violating the Same-Origin Policies and will thus taint your canvas.
Now, note that some browsers do require that the server sends at least an Vary: Origin header, or even Allow-Origin: *, even for requests that are willing to be Cross-Origin. see more.
So the best is to set the crossOrigin attribute from the first request, you don't show where it is in your code, but here are both possible cases:
onload = e => {
const img2 = new Image();
img2.crossOrigin = ""; // set it before setting the src
img2.onload = draw; // wait it's loaded
img2.src = "https://commons.wikimedia.org/static/images/project-logos/commonswiki.png";
};
function draw(evt) {
const img1 = document.getElementById('img1');
const img2 = this;
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.height = 200;
ctx.drawImage(img2,50,0,200,200);
ctx.drawImage(img1,0,0,300,200);
// export to an <img> in body
canvas.toBlob( blob =>
document.body.appendChild(new Image())
.src = URL.createObjectURL(blob)
);
}
#img1{
display: none;
}
<!-- set the attribute in the markup directly -->
<img id="img1" crossOrigin="anonymous" src="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png">
And of course, this assumes that your server is properly configured to accept anonymous requests.

Get pixel data back from programmatically generated image

I generate array of pixels in client side JavaScript code and convert it to a blob. Then I pass URL of the blob as image.src and revoke it at image.onload callback. I don't keep any references to the data, generated by the previous steps, so this data may be freed by GC.
There are many images generated this way, and it works fine. But sometimes user may want to save the generated image by clicking on a Save button near the image. I don't want to generate image again, because generation is slow, and the image is already generated and is visible on screen. So I want to get my pixels back from the image. I tried to create canvas again, draw image on it and then call toBlob, but browser treats this image as cross origin and throws an exception: "Failed to execute 'toBlob' on 'HTMLCanvasElement': tainted canvases may not be exported". Similar errors I get with canvas.toDataUrl and canvasContext.getImageData.
Is there a workaround for this problem?
I also tried to create canvases instead of images, but when I create the second canvas, the content of the first one clears.
Added
This error occurs only in the Chrome and other WebKit browsers. Firefox and MS Edge work fine. And when I commented out line of code that revoked blob url, this error disappeared - I can draw the image on the canvas and get its pixel data without CORS issues. But it is pointless to do so, because I already have blob that is not deleted.
But my page may generate many images - it depends on its user and is unlimited. Size of images is also unlimited - it may be useful to generate even 4096x4096 images. So I want to reduce memory consumption of my page as much as possible. And all these images should be downloadable. Generation of most images uses previously generated images, so to regenerate last image in a chain, I must to regenerate all images.
So I need a workaround only for Chrome browser.
Added 2
I tried to reproduce this problem in JS Fiddle, but couldn't. However locally my code doesn't work - I developed my app locally and I haven't tried running it on server. Create test.html file on your computer and open it in browser (locally, without server):
<html>
<body>
<pre id="log"></pre>
</body>
<script>
var log = document.getElementById("log");
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 256;
var ctx = canvas.getContext("2d");
canvas.toBlob(function(blob) {
var img = new Image();
var blobUrl = URL.createObjectURL(blob);
img.src = blobUrl;
img.onload = function()
{
URL.revokeObjectURL(blobUrl);
ctx.drawImage(img, 0, 0);
try { canvas.toBlob(function(blob) { log.textContent += 'success\n'; }); }
catch(e) {log.textContent += e.message + '\n';}
};
});
</script>
</html>
It will print Failed to execute 'toBlob' on 'HTMLCanvasElement': Tainted canvases may not be exported..
So, I think, my workaround is to detect that page is run on combination of WebKit browser and file:/// protocol. And I can to defer revoking blob URL until page unload only for this combination.
That indeed sounds like a bug in chrome's implementation, you may want to report it.
What seems to happen is that chrome only checks if the image has been loaded through a clean origin when it is drawn for the first time on the canvas.
Since at this time, you already did revoke the blobURI, it can't map this URI to a clean origin (file:// protocol is quite strict on chrome).
But there seems to be a simple workaround:
By drawing this image on a [the reviver]* canvas before revoking the blobURI, chrome will mark the image as clean, and will remember it.
*[edit] Actually it seems it needs to be the same canvas...
So you can simply create your export canvas before-hand, and draw each images on it (to save memory you can even set it to 1x1px when you don't use it for the export) before you do revoke the image's src:
// somewhere accessible to the export logic
var reviver = document.createElement('canvas').getContext('2d');
// in your canvas to img logic
canvas.toBlob(function(blob){
var img = new Image();
img.onload = function(){
reviver.canvas.width = reviver.canvas.height = 1; // save memory
reviver.drawImage(this, 0,0); // mark our image as origin clean
URL.revokeObjectURL(this.src);
}
img.src = URL.createObjectURL(blob);
probablySaveInAnArray(img);
};
// and when you want to save your images to disk
function reviveBlob(img){
reviver.canvas.width = img.width;
reviver.canvas.height = img.height;
reviver.drawImage(img,0,0);
reviver.canvas.toBlob(...
}
But note that this method will create a new Blob, not retrieve the previous one, which has probably been Collected by the GarbageCollector at this time. But it is unfortunately your only way since you did revoke the blobURI...

Resize cordova.camera image before sending to server [duplicate]

I am trying to paint an image on a canvas before I get its dataURL(), but the data returned is like empty.
When I check it in the console, I see there is a lot of A in the string : ("data:image/png;base64,iVBO..some random chars... bQhfoAAAAAAAAAA... a lot of A ...AAAASUVORK5CYII=")
When I try to append the canvas to the document, nothing is drawn either and I don't have any error thrown in the console.
What is the problem here ?
Here is my code :
var img = new Image();
img.src = "http://somerandomWebsite/picture.png";
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0,0); // this doesn't seem to work
var dataURL = canvas.toDataURL(); // this will give me a lot of "A"
doSomething(dataURL);
Also, when doing a quick refresh, the image gets drawn correctly onto the canvas but I've got an error message in the console and dataURL is empty.
The message in Firefox is : "SecurityError: The operation is insecure.",
in Chrome it is "Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.",
and on IE I just get "SecurityError".
What does it mean ?
You have to wait that your image has loaded before you can paint it on the canvas.
To do so, simply use the load event handler of your <img> element :
// create a new image
var img = new Image();
// declare a function to call once the image has loaded
img.onload = function(){
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var context = canvas.getContext('2d');
context.drawImage(img, 0,0);
var dataURL = canvas.toDataURL();
// now you can do something with the dataURL
doSomething(dataURL);
}
// now set the image's src
img.src = "http://somerandomWebsite/picture.png";
Also, for the canvas' context.toDataURL() and context.getImageData to work properly, you have to get your image resource in a cross-origin compliant way, otherwise the canvas is "tainted" which means that any method to get pixels data will be blocked.
If your image comes from the same server, no problem.
If your image is served from an external server, be sure that it allows yours in its cross-origin headers and set the img.crossOrigin to "use-credentials".
If the server does allow anonymous requests, you can set the img.crossOrigin to "anonymous".
Nota Bene : CORS header is sent by the server, the cross-origin attribute will only let it know that you want to use CORS to get the image data, in no way you can circumvent it if the server is not set properly.
Also some UserAgents (IE & Safari) still haven't implemented this attribute.
Edge Case : If some of your images are from your server and some are from a CORS compliant one, then you may want to use the onerror event handler which should fire if you set the cross-origin attribute to "anonymous" on a non CORS server.
function corsError(){
this.crossOrigin='';
this.src='';
this.removeEventListener('error', corsError, false);
}
img.addEventListener('error', corsError, false);

CORS error depending on an image parameters setting order

I'm using a canvas to display an image and get info on a specific portion of that image using getImageData(). Depending the order in which I set the image parameters, it works or not.
The method is called with onload on the document body. Here is the code:
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = "url_to_image";
img.crossOrigin = "anonymous";
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage( img, 0, 0 );
var data = document.getElementById('canvas').getContext('2d').getImageData( 310, 320, 1, 1 ).data
}
}
This triggers the following crossOrigin error:
Cross-origin image load denied by Cross-Origin Resource Sharing policy.
The server from which I get back the image is configured to set me back an Access-Control-Allow-Origin:* response header with the image.
However, everything works just fine if I just set the img.crossOrigin before the image.src doing :
img.crossOrigin = "anonymous";
img.src = "url_to_image";
This seems very peculiar to me. Anybody have an idea of what is happening?
.crossOrigin must come before .src
That's because .src actually starts the download.
The browser must know if anonymous access is allowed when it starts the download or it will default to not allowing cross origin access.
Cross Origin sharing is almost fully supported in modern browsers (FF,Chrome,Safari,Opera,Android). The mini version of Opera is the only major non-supporting browser.
And, as you've discovered, the server delivering the data must be configured to send appropriate headers to let the browser know that cross sharing is allowed.

Canvas element not rendering to PNG (security error) when images are on the same domain

I have two canvas:
· The first one is the canvas I want to export that contains a pattern as a background (filled through js) and some images that the user will put by clicking the canvas.
· The second one is above the first one and is used as a sort of "preview" of the images that will be pasted on the first canvas.
In a nutshell: by mousemoving on the second canvas I see the preview of the image I'll paste, while by clicking (again on the second canvas) I will parse the image on the FIRST canvas.
Ignoring the HTML code which is supposely not the problem, my script is this one:
$(window).ready(function(){
$(document).ready(function(){
$('myCanvas').ready(function(){
canvasObject = new canvasElement ('myCanvas','2d');
canvasObject.initSea();
canvasLayer1 = new canvasElement ('myCanvas2','2d');
$('#myCanvas2').on("click",function(mouseClickEvent){
$(this).off('mousemove');
var position = canvasLayer1.getMouseCoordinates(mouseClickEvent);
canvasObject.parseImage('elements/objects/wooden_turrets.png',(position.x),(position.y));
});
$('#myCanvas2').on("mousemove",function(mouseMoveEvent){
canvasLayer1.reset();
var currentPos = canvasLayer1.getMouseCoordinates(mouseMoveEvent);
canvasLayer1.parseImage('elements/objects/wooden_turrets.png',(currentPos.x),(currentPos.y));
});
$('#render').click(function() {
canvasObject.renderSVG();
});
});
});
});
function canvasElement (id, context){
this.id = id;
this.canvas = document.getElementById(id);
this.context = this.canvas.getContext(context);
}
canvasElement.prototype.parseImage = function (imgurl, x, y) {
tmpContext = this.context;
var imgObject = new Image();
imgObject.onload = function() {
tmpContext.drawImage(imgObject, x, y);
};
imgObject.src = imgurl;
};
canvasElement.prototype.reset = function () {
tmpContext = this.context;
// tmpContext.setTransform(1, 0, 0, 1, 0, 0);
tmpContext.clearRect(0, 0, this.canvas.width, this.canvas.height);
};
canvasElement.prototype.getMouseCoordinates = function (mouseEvent) {
var area = this.canvas.getBoundingClientRect();
return {
x: mouseEvent.clientX - area.left,
y: mouseEvent.clientY - area.top
};
};
canvasElement.prototype.initSea = function () {
var tmpCanvas = this.canvas;
var tmpContext = this.context;
var seaImg = new Image();
seaImg.src = 'elements/land/water.png';
seaImg.onload = function () {
var pattern = tmpContext.createPattern(seaImg,"repeat");
tmpContext.rect(0,0,tmpCanvas.width,tmpCanvas.height);
tmpContext.fillStyle = pattern;
tmpContext.fill();
}
console.log('filling');
};
canvasElement.prototype.renderSVG = function () {
var imgObject = this.canvas.toDataURL("image/png");
window.location = imgObject;
};
Now, everything is fine and is working perfectly, but whenever I'm calling the method renderSVG() which refers to the canvas method toDataURL("image/png"); I get this error:
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.
Now, I've checked a couple of questions similar to mine like:
This: toDataURL throw Uncaught Security exception
This: Get security error when saving canvas object into an image
But what I'm not getting is why it is throwing a security error since the images ARE on my domain (in this case locally), not in any other domain.
So the questions are:
1) Why is this happening? shouldn't it be exporting without any issue since the images are located on the same domain as the canvas?
2) I've looked forward thinking that, perhaps, using some libraries such as fabric.js would help me, since I've seen a lot of comfort functions but will the same error occur even using any of them?
3) Is there any way to avoid this issue?
Thanks.
Your local hard drive is considered cross-domain and your canvas is tainted.
This may seem a bit strict, but your local hard drive is where your most sensitive data probably resides.
Temporary fix #1: Move both the image and your web page html to the desktop.
Temporary fix #2: If that doesn't work, temporarily host :
open up a free account on dropbox.com
put your image(s) in the public folder of your account
right-click and get the public link to an image and use that public link in your app
when loading the image, set the crossOrigin flag to "anonymous"
Sure fix:
Host both your images and your full web app on the same web server.
For testing, You can install local versions of PHP server or IIS server depending on your O/S.
You have used crossorigin imagedata on your canvas which makes it 'Tainted'. Read about it here.

Categories