1 คะแนน โดย GN⁺ 4 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • นักพัฒนามอง 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 ความคิดเห็น

 
GN⁺ 4 시간 전
ความเห็นจาก Hacker News
  • แทนที่จะผ่านพิกเซล น่าจะใช้ SVG favicon แล้วเก็บมาร์กอัปไว้ข้างในโดยตรงก่อนค่อยดึงออกมาได้ไหม
    ใส่ข้อความอย่าง hello HN! ไว้ใน favicon.svg ใช้มันเป็น SVG favicon แล้วค่อยดึงมาแปะในเนื้อหาเอกสาร

    • ถ้าจะมองก็ไม่น่าใช่แนว “ทำไมไม่ใช้วิธีนี้แทน” แต่ควรมองว่าเป็น “มีลูกเล่นอีกแบบที่น่าสนใจ” มากกว่า ทั้งสองแบบคือการเอาเทคโนโลยีมาเล่นเพื่อความสนุก ความอยากรู้อยากเห็น และการสำรวจ ส่วนแนวทางเก็บไว้ในพิกเซลก็มีความสนุกแบบ เครื่องจักร Rube Goldberg
    • ผมเป็นคนเขียนเอง แน่นอนว่าวิธีนี้ใช้งานได้จริงกว่ามาก เพียงแต่ผมอยากให้เพย์โหลด “มีชีวิตอยู่” ใน ข้อมูลพิกเซล จริง ๆ ไม่ใช่เป็นแค่ข้อความที่ซ่อนอยู่ในไฟล์ XML เลยทำแบบนี้ :)
    • regex เหรอ อืม. แค่เข้ารหัสเป็น XML ให้ถูกต้องใน namespace ที่ถูกต้องแล้วอ่านกลับมาก็พอ
      หรือจะเสิร์ฟไฟล์ SVG ตรง ๆ แล้วฝัง HTML ไว้ข้างในเลยก็ได้ ตามทฤษฎีน่าจะ define แล้วใช้ได้ แต่เสียดายที่ทั้ง 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

  • ไม่ถึงกับต้องบอกว่า “ยังต้องมี 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...

    • อันนี้ไม่ได้ถูกแก้ไปแล้วหรืออย่างน้อยก็ถูกแก้ไปเกือบหมดแล้วเหรอ?
    • พออ่านโพสต์ต้นฉบับจบ ผมก็เผลอคิดทันทีเลยว่า “อันนี้คงถูกเอาไปใช้ทำ fingerprinting แน่ ๆ” เลยสงสัยว่ามาตรการ anti-fingerprinting เขาคำนึงถึงการจับคู่ระหว่าง favicon กับ Canvas API ด้วยหรือเปล่า
      เสียดายที่ลิงก์ไปเว็บ supercookie ใช้ไม่ได้แล้ว
  • ใน PNG มีคอมเมนต์ชังก์ tEXt, zTXt, iTXt อยู่ สามารถยัดข้อมูลได้เท่าที่ต้องการลงในไฟล์ภาพที่ภายนอกดูปกติทุกอย่างได้เลย แน่นอนว่าความสนุกอาจน้อยลงหน่อย

  • จังหวะมันบังเอิญหรือเปล่าเนี่ย? เมื่อ 1 ชั่วโมงก่อนเอง หรือพูดให้แม่นคือก่อนโพสต์นี้ 30 นาที ผมเพิ่งโพสต์ เว็บที่เก็บพอร์ตหุ้นไว้ใน URL + favicon ที่ผมทำขึ้น
    https://news.ycombinator.com/item?id=48606396

  • มันเข้ากับวิธีคิดนี้มากจริง ๆ: จอก็เป็นสตอเรจ คีย์บอร์ดก็เป็นสตอเรจ และโพสต์ในฟอรั่มก็เป็นสตอเรจ
    ถ้าค่อย ๆ แทรกการเปลี่ยนแปลงในการแก้ไขตามเวลาที่ Markov น่าจะอนุมัติได้ คุณก็จะได้ความจุเก็บข้อมูลไม่น้อยเลย แถมบางครั้งคอมเมนต์ยังน่าสนใจทางสังคมด้วย ก็เลยกลายเป็นสตอเรจที่ใช้งานได้สองทาง
    ไม่มีใครรู้ว่าตำรับ chicken casserole ของใครบางคน จริง ๆ แล้วอาจเป็น handle ของ GUID ที่ออกแบบมาอย่างประณีต และในเชิงขำ ๆ อาจชี้ไปยังโพสต์ฟอรั่มต่างกันเป็นพันโพสต์ก็ได้ ผมสงสัยว่าผู้เขียนรู้จัก PoC||GTFO ไหม เพราะนี่เป็นเทคนิคที่ดูเหมือนขุดเจอได้จากส่วนลึกของคัมภีร์ศักดิ์สิทธิ์แห่ง Alchemist Owls เลย

    • โค้ดในโค้ด วงล้อในวงล้อ
  • สไตล์การเขียนที่ตัดประโยคแรง ๆ จนดูเหมือน สร้างโดย LLM ชัดเจน ทำให้อ่านยากมาก

    • ผมเคยบ่นเรื่องสไตล์แบบนี้บน Medium เมื่อหลายเดือนก่อน ผู้เขียนตอนนั้นตอบว่าถ้าคิดว่าจะมีคนอ่านบนหน้าจอสมาร์ตโฟนเล็ก ๆ มันก็เป็นสไตล์ที่คนชอบกัน ฟังดูมีเหตุผลอยู่บ้างนะ แต่ผมก็ไม่รู้เหมือนกันว่าทั้งบทความนั้นหรือบทความนี้เป็น AI สร้างหรือเปล่า
    • ตอนอ่านไปถึงกลางบทความ ผมมั่นใจมากว่าตอนท้ายจะเฉลยว่า “จริง ๆ แล้วบทความนี้เองก็ถูกเก็บไว้ใน favicon ของเว็บไซต์” เพื่ออธิบายว่าทำไมประโยคถึงสั้นและห้วนขนาดนี้ พอรู้ว่าไม่ใช่ก็ผิดหวังจากใจจริง เป็นโอกาสที่พลาดไปเลย
    • ผมชอบวิธีเขียนแบบนี้นะ ผมเองก็เขียนคล้าย ๆ กันบ่อย และไม่เคยใช้ LLM มาสร้างงานเขียนของตัวเอง ที่ทำงานผมก็เคยเขียนแบบนี้ตรง ๆ เลย
      สำหรับผมมันดูเหมือนผู้เขียนแค่อยากเข้าเรื่องประเด็นสำคัญให้เร็ว รู้ว่าถ้าข้อความยาวเกินไป คนจะเริ่มกวาดตาอ่านผ่าน ๆ
    • นาน ๆ ทีผมจะไม่เห็นด้วยกับข้อกล่าวหาบน HN ว่านี่คือ สไตล์แบบ AI สร้าง อย่างมากก็อาจใช้ 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 จะโดนตัดตรงนั้น