canvasにCORS許可なく他のオリジンから読み込んだ画像を描画することで画像のダウンロードを防ぐ
1. 難読化した画像はダウンロードできるから直接解く
画像が送られてきているのだから、パズルのように解いてしまえばいい。でもちょっと面倒くさいナ
Alice.iconこれをやられたらお手上げだよぉ。何読化は解かれない前提
2. 何読化された画像がデコードされたらその画像をいただク♠
Alice.iconこっちをなるべく防ぎたいなぁ
...そうだ!
Alice.icon canvasをCORS汚染するためにCORSで許可していないimage(1x1pxの小さな透明な画像)をimgタグで読み込んでしまおう code:sample.ts
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx === null) {
throw new Error('Unable to get 2D context.');
}
// 画像Aを読み込む
const imgA = new Image();
imgA.onload = () => {
ctx.drawImage(imgA, 0, 0);
// 画像Bを読み込む
const imgB = new Image();
imgB.onload = () => {
ctx.drawImage(imgB, 50, 50); // この時点でcanvasは「汚染」された
try {
const dataUrl = canvas.toDataURL(); // エラー!
} catch (err) {
console.error(err);
}
};
};
元の画像をimgタグで埋め込むとDev toolでimageがわかってしまうのでなるべくバレないようにしよう
Mallory.icondocument.querySelector("canvas").toDataURL("image/jpeg")でダウンロードできなイ
1. 画像をアップロードしたら画像を返すサーバーを立てる
2. クライアントからimgA, imgBを1のサーバーにおくる
3. クライアントでdrawImageしなおした上でdocument.querySelector("canvas").toDataURL("image/jpeg")
をやるのは面倒ダ
Alice.iconが行った対策の原理
canvasを汚染するとcanvasの画像を書き出せなくなる仕様がある CORS による許可なしに他のoriginから読み込んだ何らかのデータをキャンバスに描画すると、キャンバスは汚染 (taint) されてしまいます。汚染されたキャンバスは安全とみなされなくなり、そのキャンバスから画像データを取得しようとすると、例外が発生するでしょう。 汚染されたキャンバスで以下の呼び出しを行うと、エラーが発生します。
キャンバスのコンテキストに対する getImageData() (en-US) の呼び出し
toBlob()、toDataURL()、captureStream() の <canvas> 要素自身に対する呼び出し
これを逆手にとって、canvasを意図的に汚染することによってスクレイピングを防ぐ
@a4lg: 循環論法ではあるのですが、実際の所、大手の配信サイトがこの手法をコピー防止に採用しているのを実際に見たことがあります。 そのサイトでは割と巧妙な画像難読化と併用されてたので、頑張るものだなとは思いました。
やり方
CORSで許可していないimageをimgタグで読み込む
これはdevtoolではimageとしては表示されない
content-typeはimage/jpeg
canvasにimgタグから画像を入れる