파비콘에 웹사이트를 저장한 실험
(timwehrle.de)- 개발자는 브라우저 탭 아이콘인 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,iTXtcomment chunk 사용, 여러 해상도 아이콘을 담을 수 있는ico파일 형식 사용이 있음 - 데모 사이트: https://www.timwehrle.de/labs/favicon-site/
- 구현 코드: https://github.com/timwehrle/favicon
댓글과 토론
Hacker News 의견들
-
픽셀을 거치지 말고 SVG favicon을 써서 그 안에 마크업을 직접 저장한 뒤 추출하면 되지 않나 싶음
favicon.svg에hello HN!같은 내용을 넣고, SVG favicon으로 사용한 다음 문서 본문에 추출해 붙이면 됨- “왜 대안으로 안 하냐”보다는 “재미있는 변형도 있다”로 보는 게 더 맞음. 둘 다 재미·호기심·탐구를 위해 기술을 가지고 노는 방식이고, 픽셀 안에 저장하는 접근은 루브 골드버그 장치 같은 재미가 있음
- 글쓴이인데, 물론 이 방식이 더 실용적임. 다만 페이로드가 XML 파일 안의 숨은 텍스트가 아니라 실제 픽셀 데이터 안에 “살아있게” 만들고 싶어서 이렇게 했음 :)
- 정규식이라니, 으. 올바른 네임스페이스에서 XML로 제대로 인코딩하고 그렇게 읽어오면 됨
아니면 SVG 파일을 그대로 제공하고 HTML을 임베드해서 포함해도 됨. 이론상으로는 정의한 뒤 사용할 수 있어야 하는데, 실제로는 Firefox도 Chromium도 favicon 안에서는 제대로 처리하지 않는 듯해서 아쉬움 - 개인적으로 붙들고 싸우는 풍차라서 말하자면,
[\s\S]는 더 짧고 정확하게[^]로 쓸 수 있음 - SVG는 래스터 이미지를 base64 인코딩 바이트로 임베드할 수 있음
그래서 실험을 한 겹 더 쌓아 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...- 거기에 ZIP과 PDF까지 호환되게 만들 수도 있음. https://github.com/gildas-lormeau/Polyglot-HTML-ZIP-PNG/raw/... 및 https://github.com/gildas-lormeau/Polyglot-HTML-ZIP-PNG 참고
-
사용자를 여러 도메인으로 리다이렉트하면 favicon 캐시도 저장소로 쓸 수 있음. 잠재적인 fingerprinting 위험으로 제안된 적이 있고[0], 브라우저가 시크릿 모드에서도 캐시를 순진하게 재사용하면 브라우저 프로필 간 사용자 추적에 악용될 수 있음
[0]: https://www.schneier.com/blog/archives/2021/02/browser-track...- 이건 이미 고쳐졌거나 대부분 고쳐진 것 아니었나?
- 원글을 읽자마자 본능적으로 “이건 fingerprinting에 쓰이고 있겠는데”라는 생각이 들었음. anti-fingerprinting 대책이 favicon과 Canvas API 조합까지 고려하고 있는지 궁금함
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 생성처럼 보이는 문체라 읽기가 매우 힘들었음
- 몇 달 전 Medium에서 이런 문체에 대해 불평한 적이 있음. 그 글의 저자는 작은 스마트폰 화면에서 읽힐 것을 예상하면 선호되는 스타일이라고 답했음. 어느 정도 말이 됨. 그 글이나 이 글이 AI 생성인지 아닌지는 모르겠음
- 중간쯤 읽을 때는 글 마지막에 “사실 이 글 자체가 사이트의 favicon에 저장되어 있었다”는 반전이 나와서 짧고 툭툭 끊기는 문장을 설명해 줄 거라고 확신했음. 아니라는 걸 깨닫고 진심으로 실망했음. 놓친 기회였음
- 나는 이 글쓰기 방식이 좋았음. 나도 종종 비슷하게 쓰고, 내 글을 생성하려고 LLM을 써본 적은 없음. 직장에서도 정확히 이런 식으로 쓴 적이 있음
내겐 글쓴이가 그냥 요점으로 바로 가려는 것처럼 보임. 글이 너무 많으면 사람들이 대충 훑기 시작한다는 걸 아는 것 같음 - 오랜만에 HN에서 AI 생성 문체라는 규정에 동의하지 않음. 많아야 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 코드가 그 지점에서 잘림
- 참고로 이렇게 하면서