Управление изображениями перекрестного происхождения с помощью HTML Canvas

Интерфейс HTML canvas реализует ряд функций, связанных с безопасностью, которые предотвращают междоменные манипуляции, если это явно не разрешено исходным доменом.

Что это означает на практике? Чтобы иметь возможность читать данные пикселей изображения в другом домене, хост-сервер должен сначала объявить соответствующий заголовок Access-Control-Allow-Origin. После этого потребитель сможет загрузить изображение на холст:

var img = new Image(),
    canvas = document.createElement("canvas"),
    ctx = canvas.getContext("2d"),
    src = "http://example.com/image.jpg";
img.onload = function() {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
}
img.src = src;

Однако, к сожалению, этого недостаточно. Если мы попытаемся вызвать canvas.toDataURL(), браузер пожалуется и выдаст ошибку безопасности:

Uncaught SecurityError: Failed to execute ‘toDataURL’ on ‘HTMLCanvasElement’: Tainted canvases may not be exported.

Что ?? Изображение уже отрисовано на холсте, поэтому данные должны быть там! Верно, но спецификация HTML5 представила еще один атрибут, связанный с безопасностью, в теге img. Мы должны вручную установить атрибут crossOrigin даже при использовании конструктора Image, чтобы браузер мог считывать данные пикселей обратно с холста. Раздражает то, что мы не можем сделать это по умолчанию, поскольку есть два значения, которые могут быть установлены в зависимости от контекста: anonymous, который извлекает изображения без отправки файлов cookie или заголовков аутентификации в сторонний домен, или use-credentials, который отправляет файлы cookie и заголовки аутентификации по запросу.

Итак, лучше всего проверять всякий раз, когда источник является перекрестным, и устанавливать атрибут в соответствии с вашими потребностями:

var img = new Image(),
    canvas = document.createElement("canvas"),
    ctx = canvas.getContext("2d"),
    src = "http://example.com/image.jpg";
img.onload = function() {
    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);
    console.log(canvas.toDataUrl());
}
// check if //domain.com or http://domain.com is a different origin
if (/^([\w]+\:)?\/\//.test(src) && src.indexOf(location.host) === -1) {
  img.crossOrigin = "anonymous"; // or "use-credentials"
}
img.src = src;