canvasにCORS許可なく他のオリジンから読み込んだ画像を描画することで画像のダウンロードを防ぐ
from スクレイピングを防止するWebページ
Mallory.icon 画像をグリッド状に分割して難読化したサイトの画像ダウンロード方法は2種類ある
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.src = 'http://example1.com/imageA.jpg'; // 例:ユーザーに見せたい画像。これ自体は難読化されている
imgA.onload = () => {
ctx.drawImage(imgA, 0, 0);
// 画像Bを読み込む
const imgB = new Image();
imgB.src = 'http://example2.com/imageB.jpg'; // 例:透明な画像
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")
をやるのは面倒ダ
canvasのCORS制限を突破する | WEB EGG
Alice.iconが行った対策の原理
canvasを汚染するとcanvasの画像を書き出せなくなる仕様がある
CORS による許可なしに他のoriginから読み込んだ何らかのデータをキャンバスに描画すると、キャンバスは汚染 (taint) されてしまいます。汚染されたキャンバスは安全とみなされなくなり、そのキャンバスから画像データを取得しようとすると、例外が発生するでしょう。
汚染されたキャンバスで以下の呼び出しを行うと、エラーが発生します。
キャンバスのコンテキストに対する getImageData() (en-US) の呼び出し
toBlob()、toDataURL()、captureStream() の <canvas> 要素自身に対する呼び出し
https://developer.mozilla.org/ja/docs/Web/HTML/CORS_enabled_image
これを逆手にとって、canvasを意図的に汚染することによってスクレイピングを防ぐ
@a4lg: 循環論法ではあるのですが、実際の所、大手の配信サイトがこの手法をコピー防止に採用しているのを実際に見たことがあります。
そのサイトでは割と巧妙な画像難読化と併用されてたので、頑張るものだなとは思いました。
少年ジャンプ+が実装している
https://twitter.com/Fushihara/status/1662887499625267200?s=20
やり方
CORSで許可していないimageをimgタグで読み込む
ここで画像を配信しなければいけなくなるが、その画像は画像をグリッド状に分割して難読化しておく
これはdevtoolではimageとしては表示されない
content-typeはimage/jpeg
canvasにimgタグから画像を入れる
ただしchrome dev toolでcapture node screenshotをすることでcanvasの画像全体を保存できてしまう