Hatena::ブログ(Diary)

最速チュパカブラ研究会

2011年5月10日 SVGヤバイ

canvas を苛めていたら気づいたことがあったので書きます。(この記事は2011年5月現在の情報です。あなたがこの記事を読んでいる時点で、より新しくて良い方法が無いか確認して下さい)

基本的な話

canvas には、図形を描画する機能だけでなく、描かれている内容を読み取る機能があります。getImageData を使うと、canvas の内容をピクセル単位で読み取って画像処理をかけたりできます。また、toDataURL を使うと canvas の内容を Data URI として出力でき、サーバに送信したりできます。

しかし、この機能にはリスクがあります。例えば、悪意のあるページを開いただけで、社内SNSにしか公開していないあなたの顔写真を canvas 経由で抜き取られるかもしれません。そういう事が起きないように、他のサイトから読み込んだ画像が canvas に描画されている場合(以下、canvas が汚染されている と表現)は getImageData や toDataURL が使用禁止になり、呼び出すと例外が出ます。

ここまではよく知られている話。

SVGは問答無用でダメ

しかし、同じサイトの画像であっても SVG は問答無用で「汚染」扱いになります。スクリプト内で完結している Data URI ですらダメ。 とにかく、canvas に SVG を描画した時点で読み取り禁止(図1)

f:id:gyuque:20110510152428p:image

↑図1: toDataURL が使えるかどうかのテスト(左3列はインターネット上、右3列はローカルディスク上)

理由はFrom SVG to Canvas and Backという文書に書いてあり、SVG は PNGJPEG などと違い、外部のファイルを参照できるので、SVG ファイル自体が同じサイト内にあっても安心できないということ。

これでは、アプリケーションが getImageData や toDataURL を使っている場合は SVG をアセットとして使えないという事になってしまい、デザイナの顰蹙を買う事になります。

余談: Firefox はローカルディスク上で同じディレクトリ内にあるPNG/JPEGファイルも「汚染」扱いするようですが、一方で XmlHttpRequest は許可されており(mala さんの記事参照)、しかも XmlHttpRequest でバイナリファイルを読み込むテクニックが存在するので、canvas を封じても意味が無いように思えます(試してみたところ、XmlHttpRequest → drawImage → toDataURL でPNGが読めました)

ではどうするか

先程のFrom SVG to Canvas and Back では、SVGElement にも toDataURL を実装し、さらにこれに外部を参照する要素を取り除いて「クリーンな」SVG をエクスポートする機能を付けて、このクリーンな SVG へのアクセスは許可しよう、と提案していますが、これは将来の話。今すぐ解決したい場合は canvg などの JS+Canvas で SVG をレンダリングするライブラリを使うことを薦めています。

実際に試してみたところ、確かに canvas を汚染せずに SVG をレンダリングできました(以下はIE9

f:id:gyuque:20110510155307p:image

iPhone でも動きます

f:id:gyuque:20110510160053p:image

関連記事等

特に関連しない記事