- นักพัฒนามอง favicon ซึ่งเป็นไอคอนของแท็บเบราว์เซอร์เป็นพื้นที่เก็บไบต์ระดับพิกเซล และทดลองใส่ HTML ขนาดเล็กลงในช่อง RGB ของภาพ
- วิธีเข้ารหัสคือเติมส่วนหัวความยาว 4 ไบต์ไว้หน้า UTF-8 bytes ของ HTML แล้วบันทึกแต่ละไบต์ลงในค่า R·G·B ของพิกเซลตามลำดับ
- เพย์โหลดเดโมมีขนาด 208 ไบต์ และรวมส่วนหัวเป็น 212 ไบต์ จึงใช้เพียง 71 พิกเซลที่เก็บได้พิกเซลละ 3 ไบต์ และ PNG ขนาด 9×9 ก็เพียงพอ
- การกู้คืนทำโดยวาดภาพ favicon ลงบน canvas แล้วให้ JavaScript อ่านข้อมูลพิกเซลและประกอบค่า RGB กลับเป็นอาร์เรย์ไบต์เพื่อถอดรหัสเป็น HTML
- โครงสร้างนี้ไม่ได้ทำให้เว็บไซต์รันได้อย่างอิสระด้วย favicon เพียงอย่างเดียว แต่ยังต้องมี bootstrap JavaScript แยกต่างหาก จึงใกล้เคียงการทดลองเชิงสำรวจขอบเขตมากกว่าการใช้งานจริง
วิธีปฏิบัติกับ favicon ราวกับเป็นที่เก็บข้อมูล
- favicon เป็นไอคอนเล็ก ๆ ที่แสดงในแท็บเบราว์เซอร์ แต่ในทางปฏิบัติมันคือ ไฟล์ภาพ ที่ประกอบด้วยพิกเซลและไบต์
- จุดเริ่มต้นของการทดลองมาจาก steganography แต่ในเดโมนี้เน้นใช้มันเป็นพื้นที่เก็บข้อมูลล้วน ๆ มากกว่าการทำให้ดูเหมือนไอคอน
- สิ่งที่เก็บคือเพย์โหลด HTML ขนาดเล็ก
Website in a Favicon
Everything you're reading right now was decoded from favicon pixels.
- ขั้นตอนการเข้ารหัสนั้นเรียบง่าย
- แปลง HTML เป็น UTF-8 bytes ด้วย
TextEncoder
- เติม ส่วนหัว 4 ไบต์ ที่เก็บความยาวของเพย์โหลดไว้ด้านหน้า
- เนื่องจากอาจมีพิกเซลเหลืออยู่ จึงใช้ส่วนหัวความยาวเพื่อแยกจุดสิ้นสุดที่แท้จริงของเพย์โหลด
- ไบต์แรกถูกเก็บในช่อง red ของพิกเซลแรก ไบต์ที่สองเก็บใน green และไบต์ที่สามเก็บใน blue
- จากนั้นพิกเซลถัด ๆ ไปก็ถูกเติมในลำดับเดียวกัน ทำให้เอกสาร HTML ทั้งหมดถูกใส่ไว้ในค่าของสี
- ภาพที่ได้จะดูเหมือน noise ในเชิงภาพ
ขนาดและกระบวนการกู้คืน
- ขนาดสุดท้ายของเดโมเล็กมาก
- เพย์โหลด: 208 bytes
- รวมส่วนหัว: 212 bytes
- พิกเซลที่ต้องใช้: 71 pixels
- ขนาดภาพ: 9×9 pixels
- ขนาดไฟล์: 239 bytes
- อัตราการใช้งาน: 87% {p:87}
- การกู้คืนทำได้ด้วยความสามารถของเบราว์เซอร์ล้วน ๆ
- โหลด favicon เป็นภาพ
- วาดภาพลงบน canvas
- อ่านทุกพิกเซลด้วย Canvas API
- ประกอบค่า RGB กลับเป็นอาร์เรย์ไบต์
- อ่านความยาวเพย์โหลดจาก 4 ไบต์แรก
- ดึงเพย์โหลดออกมาและถอดรหัสเป็นข้อความ UTF-8
- เว็บไซต์เดโมจะอ่าน favicon และกู้คืน HTML ก่อนแทนที่เนื้อหาของหน้าเมื่อกดปุ่ม
"Render Website"
ข้อจำกัดและทางเลือก
- ข้อจำกัดใหญ่ที่สุดคือ favicon ไม่สามารถรันทั้งเว็บไซต์ได้ด้วยตัวเอง
- favicon มี เนื้อหา ของเว็บไซต์อยู่ข้างใน
- ยังต้องมี JavaScript loader ขนาดเล็กแยกต่างหากสำหรับการถอดรหัส
- หากไม่มี JavaScript, favicon ก็เป็นเพียง PNG ที่บรรจุเนื้อหาเว็บไซต์ไว้เท่านั้น
- ในเชิงการใช้งานจริงยังไม่ค่อยเหมาะ
- ข้อมูลที่เก็บได้มีขนาดเล็กมาก
- หน้าต้องถูก bootstrap ด้วย JavaScript
- มีวิธีที่ดีกว่านี้มากในการแจกจ่ายเอกสาร HTML ขนาดเล็ก
- ทางเลือกได้แก่การใส่ markup ลงใน SVG favicon โดยตรง, ใช้ comment chunk
tEXt, zTXt, iTXt ของ PNG, หรือใช้ฟอร์แมตไฟล์ ico ที่เก็บไอคอนหลายความละเอียดได้
- เว็บไซต์เดโม: https://www.timwehrle.de/labs/favicon-site/
- โค้ดต้นฉบับ: https://github.com/timwehrle/favicon
1 ความคิดเห็น
ความเห็นจาก Hacker News
แทนที่จะผ่านพิกเซล น่าจะใช้ SVG favicon แล้วเก็บมาร์กอัปไว้ข้างในโดยตรงก่อนค่อยดึงออกมาได้ไหม
ใส่ข้อความอย่าง
hello HN!ไว้ในfavicon.svgใช้มันเป็น SVG favicon แล้วค่อยดึงมาแปะในเนื้อหาเอกสารหรือจะเสิร์ฟไฟล์ SVG ตรง ๆ แล้วฝัง HTML ไว้ข้างในเลยก็ได้ ตามทฤษฎีน่าจะ define แล้วใช้ได้ แต่เสียดายที่ทั้ง 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ไม่ถึงกับต้องบอกว่า “ยังต้องมี bootstrap loader ตัวเล็กสำหรับถอดรหัสภาพอยู่ดี” เพราะถ้าใช้ HTML/PNG polyglot ก็จัดทุกอย่างไว้ในไฟล์เดียวได้ และทุกวันนี้ฟอร์แมตใหม่อย่าง 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 น่าจะอนุมัติได้ คุณก็จะได้ความจุเก็บข้อมูลไม่น้อยเลย แถมบางครั้งคอมเมนต์ยังน่าสนใจทางสังคมด้วย ก็เลยกลายเป็นสตอเรจที่ใช้งานได้สองทาง
ไม่มีใครรู้ว่าตำรับ chicken casserole ของใครบางคน จริง ๆ แล้วอาจเป็น handle ของ GUID ที่ออกแบบมาอย่างประณีต และในเชิงขำ ๆ อาจชี้ไปยังโพสต์ฟอรั่มต่างกันเป็นพันโพสต์ก็ได้ ผมสงสัยว่าผู้เขียนรู้จัก PoC||GTFO ไหม เพราะนี่เป็นเทคนิคที่ดูเหมือนขุดเจอได้จากส่วนลึกของคัมภีร์ศักดิ์สิทธิ์แห่ง Alchemist Owls เลย
สไตล์การเขียนที่ตัดประโยคแรง ๆ จนดูเหมือน สร้างโดย LLM ชัดเจน ทำให้อ่านยากมาก
สำหรับผมมันดูเหมือนผู้เขียนแค่อยากเข้าเรื่องประเด็นสำคัญให้เร็ว รู้ว่าถ้าข้อความยาวเกินไป คนจะเริ่มกวาดตาอ่านผ่าน ๆ
เขาใช้
it’s/itsผิด, ทำBut.เป็นประโยคคำเดียว, ไม่เขียน HTML เป็นตัวพิมพ์ใหญ่, และใส่ “okayy” ในวงเล็บ ไม่ได้จะวิจารณ์ผู้เขียนนะ แต่ความไม่สมบูรณ์เล็ก ๆ พวกนี้แหละที่ประกอบเป็นบล็อกโพสต์ และทำให้ผมสนุกกับมันมากขึ้นทำให้นึกถึง real pixel coding ของ Inigo: https://www.youtube.com/watch?v=FvS_DG8yIqQ
เป็น 256-byte intro ที่ทำโดยจัดวางพิกเซลใน Photoshop แล้วบันทึกเป็น exe
เกร็ดน่าสนใจ: inline SVG แบบไหนก็ใช้เป็น favicon ได้ และยังวางไว้ตรง ๆ ในเอกสาร HTML ได้ด้วย
แบบนี้เลยทำให้ใช้อีโมจิเป็น favicon ได้โดยตรง แต่บน HN อีโมจิไม่แสดง
#rrggbbหรือลิงก์url(#id)ต้อง escape#เป็น%23ไม่งั้นมันจะถูก parse เป็น URL fragment แล้วโค้ด SVG จะโดนตัดตรงนั้น