Por que canvas.toDataURL () lança uma exceção de segurança?

90

Não dormi o suficiente ou o quê? Este código a seguir

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á gerando este erro:

SECURITY_ERR: DOM Exception 18

Não tem como isso não funcionar! Alguém pode explicar isso, por favor?

pop850
fonte
2
Essa solução não parece útil para pessoas que não usam Adobe AIR.
ObscureRobot

Respostas:

67

Nas especificações , diz:

Sempre que o método toDataURL () de um elemento de tela cujo sinalizador de limpeza de origem está definido como falso é chamado, o método deve lançar uma exceção SECURITY_ERR.

Se a imagem vier de outro servidor, acho que você não pode usar toDataURL ()

Prumo
fonte
6
Se um invasor conseguir adivinhar o nome de uma imagem que você possui em um site privado, ele poderá obter uma cópia dela pintando em uma tela e enviando a nova imagem para seu site. A principal restrição do meu ponto de vista é evitar extrair o conteúdo de outro site, mas a segurança é muito complexa, pois o invasor pode encontrar uma brecha em qualquer site inesperado.
AlfonsoML
3
Observe que o subdomínio também é importante. Na minha experiência, pelo menos no Chrome, um SECURITY_ERR: DOM Exception 18 é gerado ao fazer uma chamada que é percebida entre subdomínios: 1. em example.com/some/path/index.html para um vídeo ou imagem em foo .example.com 2. ao ir para a mesma página que em 1, mas inserindo o URL example.com/some/path/index.html e, em seguida, tentando chamar toDataUrl () para um vídeo ou imagem em www.example.com
Sam Dutton
3
@AlfonsoML, talvez eu esteja errado, mas "Se um invasor for capaz de adivinhar o nome de uma imagem que você tem em um site privado", provavelmente ele pegará a imagem com um curl sem um navegador. Meu ponto de vista: Public URL Public Content.
kilianc
4
@ pop850 Estou enfrentando esse problema mesmo quando uso um URL de dados. Existe uma maneira de contornar isso para URLs de dados?
Sujay
6
@kilianc Esta restrição existe para evitar que um invasor faça com que seu navegador (com cookies de autenticação) busque uma imagem e envie seu conteúdo a um invasor; o invasor não tem seus cookies, então ele não pode usar curl para obter os mesmos recursos que você está autorizado a obter. "URL pública, conteúdo público" é uma maneira bastante falha de pensar: minha página do Facebook tem componentes voltados para o público, mas há muitos que só podem ser acessados ​​com o token de autenticação correto.
apsillers de
18

Definir atributo de origem cruzada nos objetos de imagem funcionou para mim (eu estava 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 aqueles que usam fabricjs, veja como corrigir 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
fonte
Obrigado, funciona para mim no Chrome. Não estou usando fabric.js
Philip007
Eu uso fabricjs, mas não consegui resolver. Como você faria isso com este código? função 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
Funciona no Firefox também.
vanowm 01 de
16

Se a imagem estiver hospedada em um host que define Access-Control-Allow-Origin ou Access-Control-Allow-Credentials, você pode usar o Cross Origin Resource Sharing (CORS). Veja aqui (o atributo crossorigin) para mais detalhes.

Sua outra opção é que seu servidor tenha um endpoint que busca e veicula uma imagem. (por exemplo, http: // seu_host / endpoint? url = URL ) A desvantagem dessa abordagem é a latência e, teoricamente, a busca desnecessária.

Se houver mais soluções alternativas, estou interessado em ouvir sobre elas.

James
fonte
Olá, acabei de fazer isso, tenho Access-Control-Allow-Origin: * nas imagens, tenho crossorigin = 'anonymous', pinto a imagem na tela, chamo toDataUrl e ainda estou recebendo o SECURITY_ERR : Exceção de DOM 18
skrat
13

Parece que há uma maneira de evitar que, se a hospedagem de imagens for capaz de fornecer os seguintes cabeçalhos HTTP para os recursos de imagem e o navegador for compatível com CORS:

access-control-allow-origin: *
access-control-allow-credentials: true

É declarado aqui: http://www.w3.org/TR/cors/#use-cases

DitherSky
fonte
1
access-control-allow-credentials: true não funcionará com access-control-allow-origin: *. Quando o primeiro é definido, o último deve ter um valor de origem, não um valor curinga de developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Silver Gonzales
4

Finalmente encontrei a solução. Só precisa adicionar o crossOriginterceiro parâmetro na fromURLfunção

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

Tive o mesmo problema e todas as imagens estão hospedadas no mesmo domínio ... Então, se alguém está com o mesmo problema, resolvi aqui:

Eu tinha dois botões: um para gerar a tela e outro para gerar a imagem a partir da tela. Só funcionou para mim, e sinto muito por não saber por que, quando escrevi todo o código no primeiro botão. Então, quando eu clicar nele, gerei a tela e a imagem ao mesmo tempo ...

Sempre tenho esse problema de segurança quando os códigos estavam em funções diferentes ... = /

CarinaPilar
fonte
2

Consegui fazer funcionar usando:

Escreva isso na primeira linha do seu .htaccessservidor de origem

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

Então, ao criar um <img>elemento, faça o seguinte:

// 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
fonte
1

Você não pode colocar espaços em seu ID

Atualizar

Meu palpite é que a imagem está em um servidor diferente daquele onde você está executando o script. Consegui duplicar o seu erro ao executá-lo em minha própria página, mas funcionou bem no momento em que usei uma imagem hospedada no mesmo domínio. Portanto, está relacionado à segurança - coloque a imagem em seu site. Alguém sabe por que isso acontece?

Mike Robinson
fonte
0

Se você estiver simplesmente desenhando algumas imagens em uma tela, certifique-se de carregar as imagens do mesmo domínio.

www.example.com é diferente de example.com

Portanto, certifique-se de que suas imagens e o url que você tem em sua barra de endereço sejam iguais, www ou não.

JonathanC
fonte
0

Estou usando fabric.js e poderia resolver isso usando toDatalessJSON em vez de toDataURL:

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

Edit: Nevermind. Isso resulta na exportação apenas da imagem de fundo para JPG, sem o desenho na parte superior, portanto, não foi totalmente útil.

Horstwilhelm
fonte