icodex | 前端技术博客 | 专注 React、TypeScript、AI 与性能优化 Blog

icodex - 个人网站

马上订阅 icodex | 前端技术博客 | 专注 React、TypeScript、AI 与性能优化 Blog RSS 更新: https://icodex.me/atom.xml

canvas 污染问题

2022年6月29日 08:00

背景

前端时间实现了一个用canvas往模板图片绘制数据的功能点,遇到了一个跨域引起canvas污染的问题,仔细发掘下去发现不少的技术点。

tainted canvas

对于canvas污染的问题,这应该是非常常见的在处理跨域资源时会遇到的问题。

一般来说,利用canvas绘制图像的时候需要执行以下步骤:

  1. 创建canvas元素;
  2. 获取canvas元素的二维渲染上下文对象;
  3. 创建Image对象并加载;
  4. 等待图像加载完成的时候绘制在canvas
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");

const img = new Image();
img.src = 'xxxx';
img.onload = () => {
canvas.width = img.width;
canvas.height = img.height;

canvas.drawImage(img, 0, 0);
}

当使用drawImage方法绘制一个不同源的图像时,此时并不会报错,但是canvas会变成tainted (被污染),之后如果在当前被污染的canvas上调用以下方法时就会抛出SecurityError的错误。

警告
  • HTMLCanvasElement.toDataURL()
  • HTMLCanvasElement.toBlob()
  • CanvasRenderingContext2D.getImageData()

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

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

为什么会出现 tainted canvas

tainted canvas 其实还是牵扯到浏览器同源策略的限制问题,一般来说这个问题在使用XMLHttpRequest或者Fetch发起网络请求的情况比较多见,而在这里的目的是禁止使用canvas随意从另一个网站加载图片再转换成数据的强盗行为。

这看起来合情合理,但是现在大型网站一般都会走 CDN 服务器来缓存并代理资源访问,这就导致在动态加载图像的时候实际可能走的是 CDN 服务器域名,这就导致网页域名和资源域名不同源了。本来防别人的,这下连自己人也堵在外面了。

如何解决 tainted canvas 问题

要解决 canvas 污染的问题,需要 CORS + crossOrign 两步配置:

  1. 配置服务端响应头支持跨域请求的域名,请求方法等;
  2. 设置crossorigin属性

cors header 配置

关于服务端 CORS 的配置就不细说了,具体的可以看我的这篇文章:

提示

跨域直通车 —— 跨域与解决方案 | icodex

crossorigin

HTML 规范给crossorigin制定了三个允许值...