Como corrigir erro getImageData () A tela foi manchada por dados de origem cruzada?

131

Meu código está funcionando muito bem no meu host local, mas não está funcionando no site.

Eu recebi esse erro do console, para esta linha .getImageData(x,y,1,1).data:

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

parte do meu código:

jQuery.Event.prototype.rgb=function(){
        var x =  this.offsetX || (this.pageX - $(this.target).offset().left),y =  this.offsetY || (this.pageY - $(this.target).offset().top);
        if (this.target.nodeName!=="CANVAS")return null;
        return this.target.getContext('2d').getImageData(x,y,1,1).data;
    }

Nota: meu URL de imagem (src) é de um URL de subdomínio

Erfan Safarpoor
fonte
8
Estou recebendo esse erro mesmo quando o img.src é um URL relativo local: "img / foo.png" - então, o que é origem cruzada sobre isso?
Sanford Staab
Consulte stackoverflow.com/a/16218015/5175433 : "O Chrome não considera que arquivos locais diferentes sejam originários do mesmo domínio."
A_P 14/02

Respostas:

116

Como já foi dito, você está "contaminando" a tela carregando em um domínio de várias origens.

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

No entanto, você pode evitar isso simplesmente configurando:

img.crossOrigin = "Anonymous";

Isso funciona apenas se o servidor remoto definir o cabeçalho a seguir adequadamente:

Access-Control-Allow-Origin "*"

O seletor de arquivos do Dropbox ao usar a opção "link direto" é um ótimo exemplo disso. Eu o uso no site oddprints.com para recolher as imagens do URL da imagem da caixa de depósito remota, para a minha tela e depois enviar os dados da imagem de volta ao meu servidor. Tudo em javascript

queimaduras mate
fonte
1
Isso funcionou para corrigir este erro ao carregar uma imagem imgur em jsfiddle.net
Travis J
3
trabalhou para mim se eu colocar crossorigin fonte primeiro e depois set e, em seguida, acessar dados onload
jones
2
E se for um arquivo local e seu aplicativo não usar a Internet? Como um aplicativo android para visualização na web, a imagem está na mesma pasta que o arquivo html. Isso não resolve.
Curtis
44

Descobri que precisava usar .setAttribute('crossOrigin', '')e anexar um carimbo de data / hora à string de consulta do URL para evitar uma resposta 304 sem o Access-Control-Allow-Origincabeçalho.

Isso me dá

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
Kirby
fonte
3
Tentar isso sem um servidor em um arquivo local fornece: Acesso à imagem em 'file: /// C: /.../img/colorPaletteCtrl.png? 1507058959277' da origem 'null' foi bloqueado pela política do CORS: Resposta inválida . Portanto, a origem 'null' não é permitida.
Sanford Staab
1
Basta usar imgObj.setAttribute('crossOrigin', '')resolvido para mim - obrigado! img.crossOrigin = "Anonymous"funciona bem (como mencionado aqui ). Os navegadores @SanfordStaab tratam os arquivos locais como estando em um domínio diferente; caso contrário, os proprietários de sites podem ler seus arquivos locais. Você precisa solicitar imagens do mesmo domínio ou os cabeçalhos na resposta à sua solicitação precisam informar ao navegador que não há problema em códigos de outros domínios lerem esse arquivo.
19

Você não poderá desenhar imagens diretamente de outro servidor em uma tela e usá-las getImageData. É uma questão de segurança e a tela será considerada "contaminada".

Funcionaria para você salvar uma cópia da imagem no seu servidor usando PHP e depois carregar a nova imagem? Por exemplo, você pode enviar a URL para o script PHP e salvá-la em seu servidor e, em seguida, retornar o novo nome do arquivo para o seu javascript da seguinte maneira:

<?php //The name of this file in this example is imgdata.php

  $url=$_GET['url'];

  // prevent hackers from uploading PHP scripts and pwning your system
  if(!@is_array(getimagesize($url))){
    echo "path/to/placeholderImage.png";
    exit("wrong file type.");
  }

  $img = file_get_contents($url);

  $fn = substr(strrchr($url, "/"), 1);
  file_put_contents($fn,$img);
  echo $fn;

?>

Você usaria o script PHP com algum javascript ajax como este:

xi=new XMLHttpRequest();
xi.open("GET","imgdata.php?url="+yourImageURL,true);
xi.send();

xi.onreadystatechange=function() {
  if(xi.readyState==4 && xi.status==200) {
    img=new Image;
    img.onload=function(){ 
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    }
    img.src=xi.responseText;
  }
}

Se você usar getImageDataa tela depois disso, ela funcionará bem.

Como alternativa, se você não quiser salvar a imagem inteira, poderá passar as coordenadas x e y para o script PHP e calcular o valor rgba do pixel nesse lado. Eu acho que existem boas bibliotecas para fazer esse tipo de processamento de imagem em PHP.

Se você quiser usar essa abordagem, entre em contato se precisar de ajuda para implementá-la.

edit-1: peeps apontou que o script php está exposto e permite que a Internet o utilize potencialmente maliciosamente. existem milhões de maneiras de lidar com isso, uma das mais simples sendo algum tipo de ofuscação de URL ... eu acho que as práticas seguras de php merecem um google separado;

edit-2: por demanda popular, adicionei uma verificação para garantir que seja uma imagem e não um script php (em: PHP verifique se o arquivo é uma imagem ).

jaya
fonte
15
Como adição - Carregar a imagem e o script do sistema de arquivos local, causa o mesmo problema.
precisa saber é o seguinte
2
que de alguém não muito seguro poderia maliciosamente inundar o servidor com arquivos grandes usando o script php que seria ruim
LAX1DUDE
1
@ Resposta LAX1DUDE melhorada com uma verificação de segurança simples
jumpjack
1
Graças a este método, também é possível passar uma imagem para tesseract.js motor de OCR sem usar node.js (basta adicionar a chamada para Tesseract.recognize (img) em img.onload () função.
jumpjack
1
Você PRECISA validar o nome do arquivo, tipo de arquivo / mímica, local e tamanho dos envios fornecidos pelos usuários. Este código é vulnerável a vários ataques. Vou deixar isso como um exercício para o leitor, mas acredito que é possível fazer upload de arquivos .php para a raiz do servidor da web com esse código.
hiburn8 28/01
4

Eu estava vendo esse erro Chromeenquanto testava meu código localmente. Eu mudei para Firefoxe não estou mais vendo o erro. Talvez mudar para outro navegador seja uma solução rápida.

Se você estiver usando a solução dada na primeira resposta, adicione-o img.crossOrigin = "Anonymous";logo após declarar a imgvariável (por exemplo var img = new Image();).

Safwan
fonte
2

Seu problema é que você carrega uma imagem externa, ou seja, de outro domínio. Isso causa um erro de segurança quando você tenta acessar quaisquer dados do seu contexto de tela.

axelduch
fonte
sim, obtenho imagem do subdomínio de upload ... O que faço?
Erfan Safarpoor
1
Bem, você pode usar um proxy para recuperar sua imagem, esse proxy estaria no mesmo domínio em que você está executando sua tela.
axelduch
1
Estou carregando uma nova imagem do Meu computador e tento carregá-la na tela e estou recebendo esse erro. Não estou usando nenhum tipo de imagem do servidor.
Piyush Dholariya
seu computador é visto pelo servidor em que o script está localizado (mas não executado) como um domínio externo, pois a imagem copiada na tela é criada no seu computador, onde o script é realmente executado.
jumpjack
2

Você está "manchando" a tela carregando de um domínio de várias origens. Confira este artigo da MDN:

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

srquinn
fonte
eu adicionei <IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch "\. (cur | gif | ico | jpe? g | png | svgz? | webp) $"> SetEnvIf Origin ":" IS_CORS Header set Access- Control-Allow-Origin "*" env = IS_CORS </FilesMatch> </IfModule> </IfModule> para acessar todos os domínios, mas não funciona!
Erfan Safarpoor
1
Em que navegador você está testando? E certifique-se de reiniciar o Apache.
srquinn
Não consigo reiniciar o apache ... Tenho uma conta share cpanel.
Erfan Safarpoor
2
Não posso ajudá-lo a definir sua configuração do Apache, desculpe. Também está fora do escopo desta questão. Publique seus problemas relacionados à instalação do Apache em uma nova pergunta e a comunidade ficará feliz em ajudá-lo.
srquinn
1

Ao trabalhar no local, adicione um servidor

Eu tive um problema semelhante ao trabalhar no local. Seu URL será o caminho para o arquivo local, por exemplo, arquivo: ///Users/PeterP/Desktop/folder/index.html.

Observe que eu estou em um MAC.

Eu resolvi isso instalando o servidor http globalmente. https://www.npmjs.com/package/http-server

Passos:

  1. Instalação global: npm install http-server -g
  2. Execute o servidor: http-server ~/Desktop/folder/

PS: Eu suponho que você tenha um nó instalado, caso contrário, você não ficará muito longe executando os comandos npm.

Anas
fonte
0

Como Matt Burns diz em sua resposta , talvez seja necessário ativar o CORS no servidor em que as imagens com problemas estão hospedadas.

Se o servidor for Apache, isso pode ser feito adicionando o seguinte trecho ( daqui ) à sua configuração do VirtualHost ou a um .htaccessarquivo:

<IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
        <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
        </FilesMatch>
    </IfModule>
</IfModule>

... se adicioná-lo a um VirtualHost, você provavelmente precisará recarregar a configuração do Apache também (por exemplo, sudo service apache2 reloadse o Apache estiver executando em um servidor Linux)

Nick F
fonte
0

Meu problema estava tão ruim que eu apenas codifiquei a imagem base64 para garantir que não houvesse problemas com o CORS

prancha de surf
fonte
-2

Hoje, encontro o mesmo problema e resolvo-o pelo código a seguir.

Código HTML:

<div style='display: none'>
  <img id='img' src='img/iak.png' width='600' height='400' />
</div>
<canvas id='iak'>broswer don't support canvas</canvas>

código js:

var canvas = document.getElementById('iak')
var iakImg = document.getElementById('img')
var ctx = canvas.getContext('2d')
var image = new Image()
image.src=iakImg.src
image.onload = function () {
   ctx.drawImage(image,0,0)
   var data = ctx.getImageData(0,0,600,400)
}

código como acima, e não há problema entre domínios.

Karry
fonte
1
O valor src ainda é uma fonte de origem cruzada. Como isso evita o problema?
Loveen Dyall
Essa abordagem funciona ao executar um arquivo html local (sem servidor), como arquivo: /// X: /htmlfile.html. A maioria das pessoas termina suas declarações com ";". Qual é o objetivo da instrução "var data = .."? Eu gostaria de uma explicação de por que isso evita o erro "entre domínios". Mas sim, obrigado.
dcromley
dobrar karry inteligente
Mari Selvan
1
@dcromley "A maioria das pessoas termina suas declarações com;" O uso de ponto e vírgula não é recomendado de acordo com os padrões. O ponto e vírgula adiciona mais trabalho, parece feio e o código funciona perfeitamente sem eles. Verifique github.com/standard/standard
madprops
1
@madprops developer.mozilla.org diz "Os aplicativos JavaScript consistem em instruções com uma sintaxe apropriada. Uma única instrução pode abranger várias linhas. Podem ocorrer várias instruções em uma única linha se cada instrução for separada por ponto e vírgula. Esta não é uma palavra-chave , mas um grupo de palavras-chave ". Então, eu estou na facção "use sempre". Eu nem sabia que havia uma facção "nunca". Suponho que este último também acredite que a terra é plana? (Brincadeirinha - graças)
dcromley
-3

Eu estava tendo o mesmo problema e, para mim, funcionou simplesmente concatenando https:${image.getAttribute('src')}

Livia Rett
fonte
por favor, explique o que você fez
Yehuda Schwartz