- 開発者は、ブラウザのタブアイコンである favicon をピクセル単位のバイト格納領域と見なし、小さなHTMLを画像のRGBチャネルに入れる実験を行った
- エンコードは、HTMLの UTF-8バイト の先頭に4バイトの長さヘッダーを付け、各バイトをピクセルのR・G・B値へ順番に記録する方式
- デモのペイロードは208バイトで、ヘッダー込みで212バイトだったため、3バイトずつ格納する71ピクセルと 9×9 PNG で十分だった
- 復元は、favicon画像をcanvasに描画したあと、JavaScriptがピクセルデータを読み取り、RGB値を再びバイト配列に組み立ててHTMLとしてデコードする
- faviconだけでウェブサイトが単独実行される構造ではなく、別途 bootstrap JavaScript が必要なため、実用性よりも境界的な実験に近い
faviconをストレージのように扱う方法
- faviconはブラウザのタブに表示される小さなアイコンだが、実際にはピクセルとバイトで構成された 画像ファイル である
- 実験の出発点はステガノグラフィーだったが、デモではアイコンらしく見せることより、純粋な保存領域として使うことに重点が置かれている
- 保存対象は小さなHTMLペイロードである
Website in a Favicon
Everything you're reading right now was decoded from favicon pixels.
- エンコード手順は単純
TextEncoder でHTMLを UTF-8バイト に変換する
- ペイロード長を入れた 4バイトヘッダー を先頭に付ける
- 余るピクセルが出る可能性があるため、長さヘッダーで実際のペイロード終端を判別する
- 1バイト目は最初のピクセルのredチャネル、2バイト目はgreen、3バイト目はblueに保存される
- 以降のピクセルも同じ順序で埋められ、HTML文書全体が色の値として入る
- 結果の画像は見た目にはノイズのように見える
サイズと復元プロセス
- デモの最終サイズは非常に小さい
- ペイロード: 208 bytes
- ヘッダー込み総量: 212 bytes
- 必要なピクセル数: 71 pixels
- 画像サイズ: 9×9 pixels
- 容量: 239 bytes
- 使用率: 87% {p:87}
- 復元はブラウザ機能だけで処理される
- faviconを画像としてロードする
- 画像をcanvasに描画する
- Canvas APIですべてのピクセルを読み取る
- RGB値をバイト配列として再構成する
- 先頭4バイトからペイロード長を読む
- ペイロードを抽出し、UTF-8テキストとしてデコードする
- デモサイトでは
"Render Website" ボタンを押すとfaviconを読み込み、HTMLを復元してからページ内容を置き換える
制約と代替案
- 最大の制約は、faviconがウェブサイト全体を単独では実行できないことにある
- faviconにはウェブサイトの コンテンツ が入っている
- デコード用の小さなJavaScriptローダーが別途必要
- JavaScriptがなければ、faviconはウェブサイトの内容を含むPNGにすぎない
- 実用性は低い
- 保存できるデータ量が非常に小さい
- ページはJavaScriptでブートストラップされる必要がある
- 小さなHTML文書を配布する方法は他にももっとよいものが多い
- 代替案としては、SVG faviconにmarkupを直接入れる方法、PNGの
tEXt、zTXt、iTXt comment chunk を使う方法、複数解像度のアイコンを格納できる ico ファイル形式を使う方法がある
- デモサイト: https://www.timwehrle.de/labs/favicon-site/
- 実装コード: https://github.com/timwehrle/favicon
1件のコメント
Hacker News のコメント
ピクセルを経由せずに SVG favicon を使って、その中にマークアップを直接保存してから取り出せばいいのでは、と思った
favicon.svgにhello HN!のような内容を入れ、SVG favicon として使ったあと、文書本文に抽出して貼り付ければよいあるいは SVG ファイルをそのまま提供し、HTML を埋め込んで含めてもよい。理論上は定義してから使えるはずだが、実際には Firefox も Chromium も favicon の中ではきちんと処理しないようで残念
[\s\S]はもっと短く、しかも正確に[^]と書けるなので実験をさらに一段重ねて、favicon は SVG、その中にエンコードされたラスターがあり、そのバイト列の中に HTML がエンコードされている構造も可能。少なくとも気が遠くなるような CTF の段階にはなりそう
もちろん新しいアイデアではない。たとえば 2000 年には誰かが deCSS を favicon に保存していた
https://web.archive.org/web/20010408040524if_/http://decss.z...
抽出は
dd bs=1 skip=2238 < favicon.icoで可能「画像をデコードするための小さなブートストラップローダーが依然として必要だ」という話ではない。HTML/PNG ポリグロット を使えば単一ファイルですべて処理でき、今なら WebP のような新しい形式で圧縮率もさらに良くできるかもしれない
https://web.archive.org/web/20120801001616/http://daeken.com...
ユーザーを複数のドメインにリダイレクトすれば、favicon キャッシュ もストレージとして使える。潜在的な fingerprinting リスクとして提案されたことがあり[0]、ブラウザがシークレットモードでもキャッシュを安易に再利用するなら、ブラウザプロファイル間でのユーザー追跡に悪用されうる
[0]: https://www.schneier.com/blog/archives/2021/02/browser-track...
supercookie サイトへのリンクは残念ながら死んでいる
PNG には tEXt, zTXt, iTXt コメントチャンクがある。見た目には完全に普通の画像ファイルのまま、好きなだけ内容を詰め込める。もちろん面白さは少し減るだろうが
これはタイミングの偶然? つい1時間前、正確にはこの記事の30分前に、私が作った 株式ポートフォリオを URL + favicon に保存するサイト を投稿した
https://news.ycombinator.com/item?id=48606396
“Pong in S Favicon”
https://news.ycombinator.com/item?id=48608681
この考え方には本当によく合っている。モニターもストレージ だし、キーボードもストレージ、フォーラムの投稿もストレージだ
時間をかけて編集に Markov が承認しそうな変形を加えれば、かなりの保存容量が得られる。しかもコメントはときどき社会的にも興味深いので、二重用途のストレージになる。
誰かのチキンキャセロールのレシピが、実は巧妙に構成された GUID のハンドルで、冗談半分に言えば千個の異なるフォーラム投稿を指しているのかもしれないが、誰にもわからない。筆者が PoC||GTFO を知っているのか気になるが、これはまさに Alchemist Owls の聖典の奥深くにありそうな技法だ
攻撃的に短く切る、明らかに LLM 生成 のように見える文体で、とても読みにくかった
私には、筆者はただ要点にすぐ入りたいだけに見える。文章が多すぎると人は流し読みし始めるとわかっているのだと思う
it’s/itsを間違えていたし、But.を一語だけの文にしていたし、HTML を大文字で書いていなかったし、括弧内に “okayy” と書いていた。筆者を批判したいわけではなく、こうした小さな不完全さがブログ記事を形作っているのが見えて、むしろより楽しかったInigo の real pixel coding を思い出した: https://www.youtube.com/watch?v=FvS_DG8yIqQ
Photoshop でピクセルを配置し、exe として保存して作った 256 バイトイントロだ
面白い事実として、どんな インライン SVG でも favicon にでき、HTML 文書内にそのまま置いておける
こうすると絵文字を favicon として直接使うことも可能。HN では絵文字は表示されない
#rrggbbの色コードやurl(#id)リンクを使いたいなら、#を%23にエスケープする必要がある。そうしないと URL fragment として解釈され、SVG コードがその地点で切れてしまう