- 개발자는 브라우저 탭 아이콘인 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 페이로드임
<h1>Website in a Favicon</h1>
<p>
Everything you're reading right now was decoded from favicon pixels.
</p>
- 인코딩 절차는 단순함
- TextEncoder로 HTML을 UTF-8 바이트로 변환함
- 페이로드 길이를 담은 4바이트 헤더를 앞에 붙임
- 남는 픽셀이 있을 수 있어 길이 헤더로 실제 페이로드의 끝을 구분함
- 첫 번째 바이트는 첫 픽셀의 red 채널, 두 번째 바이트는 green, 세 번째 바이트는 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