¿Por qué canvas.toDataURL () lanza una excepción de seguridad?

90

¿No dormí lo suficiente o qué? Este siguiente código

var frame=document.getElementById("viewer");
frame.width=100;
frame.height=100;

var ctx=frame.getContext("2d");
var img=new Image();
img.src="http://www.ansearch.com/images/interface/item/small/image.png"

img.onload=function() {
    // draw image
    ctx.drawImage(img, 0, 0)

    // Here's where the error happens:
    window.open(frame.toDataURL("image/png"));
}

está lanzando este error:

SECURITY_ERR: DOM Exception 18

¡No hay forma de que esto no funcione! ¿Alguien puede explicar esto, por favor?

pop850
fuente
Consulte aquí para obtener una solución: ¿Cómo usar canvas.toDataURL () para obtener base64 de la imagen en Adobe AIR?
Danny Beckett
2
Esa solución no parece útil para las personas que no usan Adobe AIR.
ObscureRobot

Respuestas:

67

En las especificaciones dice:

Siempre que se llama al método toDataURL () de un elemento de lienzo cuyo indicador de limpieza de origen se establece en falso, el método debe generar una excepción SECURITY_ERR.

Si la imagen proviene de otro servidor, no creo que pueda usar toDataURL ()

Beto
fuente
6
Si un atacante puede adivinar el nombre de una imagen que tienes en un sitio privado, podría obtener una copia pintando en un lienzo y enviando la nueva imagen a su sitio. La principal restricción desde mi punto de vista es evitar dibujar el contenido de otro sitio, pero la seguridad es demasiado compleja ya que el atacante puede encontrar un hueco en cualquier sitio inesperado.
AlfonsoML
3
Tenga en cuenta que el subdominio también es importante. En mi experiencia, al menos en Chrome, se genera una SECURITY_ERR: DOM Exception 18 cuando se realiza una llamada que se percibe como entre subdominios: 1. en example.com/some/path/index.html para un video o imagen en foo .example.com 2. cuando vaya a la misma página que en 1 pero ingrese la URL example.com/some/path/index.html y luego intente llamar a toDataUrl () para obtener un video o una imagen en www.example.com
Sam Dutton
3
@AlfonsoML, tal vez me equivoque, pero "si un atacante es capaz de adivinar el nombre de una imagen que tienes en un sitio privado" probablemente tomará la imagen con un curl non con un navegador. Mi punto de vista: Contenido público de URL públicas.
kilianc
4
@ pop850 Me enfrento a este problema incluso cuando uso una URL de datos. ¿Hay alguna forma de solucionar esto para las URL de datos?
Sujay
6
@kilianc Esta restricción existe para evitar que un atacante haga que su navegador (con cookies de autenticación) obtenga una imagen y envíe su contenido a un atacante; el atacante no tiene sus cookies, por lo que no puede usar curl para obtener los mismos recursos que usted está autorizado a obtener. "URL pública, contenido público" es una forma de pensar bastante defectuosa: mi página de Facebook tiene componentes públicos, pero hay muchas cosas a las que solo se puede acceder con el token de autenticación correcto.
apsillers
18

Establecer el atributo de origen cruzado en los objetos de imagen funcionó para mí (estaba usando fabricjs)

    var c = document.createElement("img");
    c.onload=function(){
        // add the image to canvas or whatnot
        c=c.onload=null
    };
    c.setAttribute('crossOrigin','anonymous');
    c.src='http://google.com/cat.png';

Para aquellos que usan fabricjs, aquí se explica cómo parchear Image.fromUrl

// patch fabric for cross domain image jazz
fabric.Image.fromURL=function(d,f,e){
    var c=fabric.document.createElement("img");
    c.onload=function(){
        if(f){f(new fabric.Image(c,e))}
        c=c.onload=null
    };
    c.setAttribute('crossOrigin','anonymous');
    c.src=d;
};
Philip Nuzhnyy
fuente
Gracias, me funciona en Chrome. Aunque no estoy usando fabric.js
Philip007
Utilizo fabricjs pero no pude resolverlo. ¿Cómo harías esto con este código? function wtd_load_bg_image (img_url) {if (img_url) {var bg_img = new Image (); bg_img.onload = function () {canvasObj.setBackgroundImage (bg_img.src, canvasObj.renderAll.bind (canvasObj), {originX: 'left', originY: 'top', left: 0, top: 0}); }; bg_img.src = img_url; }}
HOY
También funciona en Firefox.
vanowm
16

Si la imagen está alojada en un host que configura Access-Control-Allow-Origin o Access-Control-Allow-Credentials, puede utilizar el uso compartido de recursos de origen cruzado (CORS). Consulte aquí (el atributo crossorigin) para obtener más detalles.

Su otra opción es que su servidor tenga un punto final que obtenga y sirva una imagen. (por ejemplo, http: // your_host / endpoint? url = URL ) La desventaja de ese enfoque es la latencia y la búsqueda teóricamente innecesaria.

Si hay más soluciones alternativas, me interesaría conocerlas.

James
fuente
Hola, hice precisamente eso, tengo Access-Control-Allow-Origin: * en las imágenes, tengo crossorigin = 'anonymous', luego pinto la imagen en el lienzo, luego llamo a toDataUrl y todavía obtengo el SECURITY_ERR : DOM Exception 18
skrat
13

Parece que hay una manera de evitar que si el alojamiento de imágenes puede proporcionar los siguientes encabezados HTTP para los recursos de imagen y el navegador es compatible con CORS:

acceso-control-permitir-origen: *
acceso-control-permitir-credenciales: verdadero

Se indica aquí: http://www.w3.org/TR/cors/#use-cases

DitherSky
fuente
1
access-control-allow-credentials: true no funcionará con access-control-allow-origin: *. Cuando se establece el primero, el último debe tener un valor de origen, no un valor comodín de developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Silver Gonzales
4

Finalmente encontré la solución. Solo necesito agregar el crossOrigintercer parámetro en fromURLfunc

fabric.Image.fromURL(imageUrl, function (image) {
            //your logic
    }, { crossOrigin: "Anonymous" });
jose920405
fuente
2

Tuve el mismo problema y todas las imágenes están alojadas en el mismo dominio ... Entonces, si alguien tiene el mismo problema, así es como lo resolví:

Tenía dos botones: uno para generar el lienzo y otro para generar la imagen a partir del lienzo. Solo funcionó para mí, y lo siento, no sé por qué, cuando escribí todo el código en el primer botón. Entonces, cuando hago clic en él, genero el lienzo y la imagen al mismo tiempo ...

Siempre tengo este problema de seguridad cuando los códigos estaban en diferentes funciones ... = /

CarinaPilar
fuente
2

Pude hacerlo funcionar usando esto:

Escriba esto en la primera línea de su .htaccessservidor de origen

Header add Access-Control-Allow-Origin "*"

Luego, al crear un <img>elemento, hágalo de la siguiente manera:

// jQuery
var img = $('<img src="http://your_server/img.png" crossOrigin="anonymous">')[0]

// or pure
var img = document.createElement('img');
img.src='http://your_server/img.png';
img.setAttribute('crossOrigin','anonymous');
QWERTY
fuente
1

No puedes poner espacios en tu identificación

Actualizar

Supongo que la imagen está en un servidor diferente al que está ejecutando el script. Pude duplicar su error al ejecutarlo en mi propia página, pero funcionó bien en el momento en que usé una imagen alojada en el mismo dominio. Por lo tanto, está relacionado con la seguridad: coloque la imagen en su sitio. ¿Alguien sabe por qué este es el caso?

Mike Robinson
fuente
0

Si simplemente está dibujando algunas imágenes en un lienzo, asegúrese de cargar las imágenes del mismo dominio.

www.example.com es diferente a example.com

Así que asegúrese de que sus imágenes y la URL que tiene en la barra de direcciones sean las mismas, www o no.

JonathanC
fuente
0

Estoy usando fabric.js y podría resolver esto usando toDatalessJSON en lugar de toDataURL:

canvas.toDatalessJSON({ format: 'jpeg' }).objects[0].src

Editar: No importa. Esto da como resultado que solo la imagen de fondo se exporta a JPG, sin el dibujo en la parte superior, por lo que no fue del todo útil después de todo.

horstwilhelm
fuente