I Stored a Website in a Favicon
TL;DR · AI 摘要
通过将HTML代码编码进favicon像素,实现网站数据的隐藏存储,技术可行且图像体积小。
核心要点
- 将208字节的HTML代码编码进9x9像素的favicon中,仅使用87%的容量。
- 使用TextEncoder将HTML转换为字节,并添加4字节的长度头以确保数据完整性。
- 浏览器通过canvas API读取favicon像素,可还原原始HTML内容。
结构提纲
按章节快速跳转。
- §引言
作者受到之前在鼠标DPI寄存器中隐藏数据的启发,开始探索favicon作为数据存储的可能性。
- ·核心机制
通过将HTML代码转换为字节,并将其写入favicon像素的RGB通道,实现数据隐藏。
使用TextEncoder将HTML转换为字节,并添加长度头,再将字节写入像素通道。
最终生成的9x9像素favicon存储了208字节的HTML内容,使用了87%的容量。
- ›读取数据
通过浏览器的canvas API读取favicon像素,可还原原始HTML内容。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- favicon数据存储
- 核心机制
- HTML转字节
- 添加长度头
- 写入RGB通道
- 结果
- 9x9像素图像
- 208字节HTML
- 87%容量使用
- 读取方法
- 加载favicon为图像
- 绘制到canvas
- 使用canvas API还原HTML
金句 / Highlights
值得收藏与分享的关键句。
The payload ended up being 208 bytes. Adding the 4-byte header brings the total to 212 bytes.
Every pixel has red, green and blue values. That's three bytes. If I wanted to store text, I could just take the UTF-8 bytes of the text and write them directly into the RGB channels.
The browser doesn't care what those bytes represent. To the browser they're colors. To me in this case they're HTML.
我把一个网站存进了favicon里
返回
实验
|
2026年6月19日
4分钟阅读
我把一个网站存进了favicon里
不久前,我写过关于在鼠标DPI寄存器中存储两个字节的内容。这并没有什么用处,也不实际。但它对我的大脑产生了一种不幸的影响。一旦你成功地将数据隐藏在了不该存在的地方,你开始把一切看作潜在的存储介质。
显示器是存储。
键盘是存储。
BIOS启动画面(也许)是存储。
一个favicon也是存储。
没错,我们正处在这个情境中。
每个网站都有一个favicon。它是你浏览器标签页中的那个小图标。通常你上传一次后就再也不用考虑它了。但是,一个favicon只是一个图像。一个图像只是像素。而像素只是字节。
所以当然,我开始思考是否能将一些东西存储在其中。
思路
我最初的念头是隐写术。
隐写术基本上是关于在图像中隐藏数据而不让其显而易见。你拿一张完美的正常照片,修改几个比特,让它秘密地包含一条信息。
在演示中,favicon本身(至少在我的演示中)不需要看起来像一个图标。它可以变成纯粹的存储。
每个像素都有红色、绿色和蓝色的值。这是三个字节。如果我想存储文本,我可以直接将文本的UTF-8字节写入RGB通道。
浏览器并不关心这些字节代表什么。对浏览器来说,它们是颜色。而对我来说,它们是HTML。
构建一个favicon网站
我从一个微小的HTML负载开始:
<h1>Website in a Favicon</h1>
<p>
Everything you're reading right now was decoded from favicon pixels.
</p>这个过程相当直接。
首先,我使用TextEncoder将HTML转换为字节。
然后,我添加四个字节,包含负载长度。
长度头很重要,因为图像本身可能在末尾包含未使用的像素。如果没有长度值,就无法知道真正的负载在哪里结束。
然后,我开始填充像素:第一个字节变成第一个像素的红色通道,第二个变成绿色,第三个变成蓝色,然后是下一个像素,再下一个,再下一个,直到整个HTML文档以彩色像素的形式存在。结果看起来像视觉噪声。
非常小
说实话,最让我惊讶的并不是它能工作,而是结果图像有多小。
负载最终是208字节。
加上4字节的头,总共有212字节。
因为每个像素存储三个字节,我需要:
- 总共212字节
- 71个像素
- 一个足够大的正方形图像来容纳它们
能工作的最小正方形是9x9像素。
这只有81个像素。
最终的统计数据如下:
- 负载:208字节
- 图像大小:9x9像素
- 容量:239字节
- 使用率:87%
不知怎么地,一个完整的网站(好吧,带有一些样式的HTML)竟然能放进一个比通常favicon还要小的图像中。
将网站读取回来
存储数据只是问题的一半。另一半是将它取回。
浏览器已经具备了完成这项任务所需的一切。
- favicon作为图像加载。
- 图像被绘制到画布上。
- 画布API允许JavaScript读取每个像素。
一旦我有了像素数据,我只需简单地逆向这个过程。
- 读取RGB值。
- 重建字节数组。
- 读取前四个字节以确定负载长度。
- 提取负载。
- 解码UTF-8文本。
到那时,我又有了原始的HTML。
浏览器通过自身的 favicon 读取了一个网站。
重要的注意事项
favicon 本身并不包含整个网站的内容。
它只包含网站内容的一部分。
你仍然需要一个极小的引导加载器来解码图像。
没有 JavaScript,favicon 只是一个 PNG(其中包含你的网站内容)。
为了演示这个场景,该网站包含一个“渲染网站”按钮。它会读取 favicon,解码 HTML,并用重建后的内容替换页面。
这是否有用?
当然没有。
你能存储的数据量非常有限。页面需要 JavaScript 来引导自身。还有数十种更有效的方式来分发一个小型 HTML 文档。
但归根结底,这关乎于测试边界,对吧?
favicon 看起来是一个非常具体的东西。它应该只是一个图标。
但最终,它只是一个 PNG。
而一个 PNG 文件基本上只是字节。
这可能就是我构建过的最小的网站……
替代方法
- 直接在 SVG favicon 中存储标记,并在页面加载时读取它。
- 使用 PNG 的注释块,如 tEXt、zTXt 和 iTXt。
- 使用 ico 文件格式,因为它允许包含不同分辨率的多个图标。
这是网站的链接:https://www.timwehrle.de/labs/favicon-site/
如果你想看看它是如何运作的:https://github.com/timwehrle/favicon