試し方
- 「元画像の選択」から画像を選択します.
- 「保存」ボタンをクリックし, 隠蔽済画像をダウンロードします.
- 「画像の復元」から上でダウンロードした画像を読み込みます.
- 元画像が表示されることを確認します.
- スタイル設定を変更したり画像を別ウインドウに表示したりして画像が隠蔽されていることを確認します.
Firefox及びChromeでのみ動作を確認しています. Firefoxで作成した画像をChromeで読み込む, もしくはその逆も可能です.
※Edgeについてはimg/canvas要素におけるSVGフィルタの参照をサポートしないため正しく動作しません. なおSVGのimage要素であればうまく行くかもしれません(未検証).
※Safariについては未確認です.
基本原理
元となる画像に事前に用意しておいたノイズ画像を加算・除去するフィルタを用意し, WEBブラウザ表示時にそれぞれのフィルタ効果が打ち消し合うことで元画像が復元されるようにします. 式で表すなら元画像を\(G\), ノイズを加算するフィルタを\(e\), ノイズを除去するフィルタを\(d\)とすると, \(G = d(e(G))\)のように記述できます.
ここでWEBサーバーには\(e(G)\)なる画像を配置しておき, WEBブラウザで表示する際に(CSSによって)フィルタ\(d\)が適用されるようにすると, ブラウザスクリーン上でのみ元画像が表示, つまり「\(G = d(e(G))\)」の関係が成立します. 一方意図しないルートで画像が流出したとしても, 拡散した画像\(e(G)\)にはノイズが載っているため元画像が判りません.
フィルタ構成の実際
先ほどの原理は理想のもので, 実際には仕様上\(G = d(e(G))\)のような元画像を完全に復元するようなフィルタの対を構成することは出来ず, \(d(e(G))\)で得られる画像は\(G\)を減色した\(G'\)となります.
以下は本ページで利用しているフィルタです. feTurbulence要素が画像を隠蔽するためのノイズ画像を生成している原始フィルタで, 後続のfeCompositeがノイズ画像の加算・除去を行う本体です.
各構成要素の関連を図にすると次のようになります.
なおノイズの比率を上げた場合復元画像の品質が落ちる一方で, 目視による判読のリスクが軽減されます.
特徴
- 隠蔽画像の生成にはHTML5のcanvas APIを利用しています. 完璧に同等の画像処理を施せるのであれば言語・環境は問いません.
- 画像の復元にはスクリプトが必要ありません. CSSだけで動作します.
- feTurbulence要素が生成するノイズ画像を画像復元のキーとして利用します. そのため隠蔽済画像から元画像を復元するには, ノイズを生成した際のパラメータ(feTurbulence要素のbaseFrequency/seed属性)を渡すだけで済みます. つまり属性値が暗号化キーの役割を果たしています. また, フィルタ構造そのものをカスタマイズすることも可能です.
- 仕組み上, 隠蔽済画像単体から元画像を復元するのは極めて困難です. 従って意図しないネットワークにおけるパケットスニッフィング・WEBプロキシによる画像データの蓄積・ブラウザキャッシュの参照・ブラウザを用いた画像の保存・開発者ツールを使った分析に対してもデータ流出に対する耐性を持ちます.
- 加えてフィルタ設定と組み合わせて元画像を復元するにはSVGフィルタの仕様を理解し, 適切なツールを利用する必要があります. つまりカジュアルコピーのリスクが大幅に軽減されます. ※とは言えスクリーンキャプチャには無力です.
注意点・課題等
- Firefox(Gecko)とChrome(Blink)とでfeTurbulenceが生成するノイズ画像に微細なズレ(Firefoxと比べ, Chromeでは縦横1pxずれている)があります. そのため, ノイズを除去する過程でブラウザ毎にこのズレを取り除く必要があります.
- フィルタを適用するimg要素にはborder等のフィルタ基準をずらすようなスタイルを適用することが出来ません. Chromeでは同様にtransformによる変形でノイズ除去に失敗します.
- ブラウザのズームによりノイズ画像の精細度が上がるため, 画像の復元に失敗します.
- Retina環境ではノイズ画像の精細度が上がるため, ノイズを正しく除去することが出来ないものと思われます.
- ノイズを加算するにあたり元画像を減色する必要があります. また, 減色(に起因するRGB値の丸め)に伴い元画像の鮮明さが失われます.
例) \(255\div20=12.5\overset{画像保存}{→}12\overset{ブラウザ表示}{→}12\times20=240\)そのため必要に応じ色調補正などのフィルタを追加すると良いでしょう. またブラウザ毎のフィルタの動作特性によって綺麗な結果が得られないことがあります. なお減色を伴わずに元画像を完全に維持するノイズの加算・除去をフィルタ処理で実現することは出来ません. (グレイスケールなどの条件を付けるのであれば可能かもしれません)
…つまり白色:RGB(255,255,255)はもとより暗いRGB(240,240,240)色に復元されます. - ノイズを加算した画像にJPEG等の非可逆圧縮が為されると, それに伴う微細なノイズがフィルタによって増幅されるため画像の復元がうまく行きません. 従って画像の保存形式には原則PNGを利用する必要がありますが, その結果データサイズが無闇に肥大します. なお圧縮ノイズに強いフィルタの存在やそれを作るための条件については不明です.
- コントラストの高い画像(例えば文字をスキャンした2値画像)の場合, フィルタのパラメータのとり方によってはノイズを加算しても判読出来てしまうことがあります.
- HTMLソースやCSSコードを確認することで, (自明ではないものの)画像に施されているフィルタから逆フィルタを算出し, 画像データが復元される可能性があります. 従ってより慎重を期すのであればShadow DOMを用いてスタイル設定そのものを隠蔽する方法も考えられます.
- img(canvas)要素のレイアウトによってはスクリーングリッドに対して1px以下の誤差が発生します. この誤差によってblink系ブラウザではCSSフィルタによる画像復元に失敗します. この問題はgetBoundingClientRectで得たcanvas要素の位置から小数点以下の誤差を吸収するtransformプロパティを追加することで解消します.
応用等
- ノイズ画像はfeTurbulence要素で生成する必要はなく, 別途画像ファイルとして用意することも可能です. 例えば画像にコピーライトなどの権利表記を施しておき, WEBページで表示している時に限り表記をキャンセルすると言った用途にも用いることが可能です.
- ノイズ画像をcanvas要素で生成する方法もあります. この場合パーリンノイズと同様に再現性のある乱数アルゴリズムを用います.
- canvas要素と組み合わせることも出来ます. つまり, CanvasRenderingContext2D.filterプロパティとCSSのfilterプロパティの双方に互いに打ち消し合うフィルタを施しておくことで, グラフィック描画アルゴリズムに一切手を入れることなく画像データを保護することが出来ます.