2 คะแนน โดย GN⁺ 2025-02-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • หากนำ variation selector ของ Unicode มาเรียงต่อกัน จะสามารถซ่อนลำดับไบต์ไว้หลังอักขระหนึ่งตัวได้ แม้จะไม่แสดงบนหน้าจอ แต่ยังติดไปกับการคัดลอก·วาง
  • มี variation selector ทั้งหมด 256 ตัว ตั้งแต่ VS-1 ถึง VS-256 จึงสร้างการแมปที่ตรงกับช่วง 1 ไบต์ได้พอดี
  • แม้จะต่อท้าย 😊 ด้วยไบต์ของ hello คือ [0x68, 0x65, 0x6c, 0x6c, 0x6f] ภายนอกก็ยังดูเหมือนอีโมติคอนธรรมดาหนึ่งตัว
  • การถอดรหัสทำได้โดยค้นหาช่วง U+FE00..U+FE0F และ U+E0100..U+E01EF แล้วแปลงกลับเป็นไบต์ โดย อักขระฐาน ไม่จำเป็นต้องเป็นอีโมติคอนก็ได้
  • วิธีนี้เป็นการใช้ Unicode ในทางที่ผิด และอาจถูกนำไปใช้หลบเลี่ยงตัวกรองคอนเทนต์ของมนุษย์ หรือฝัง ลายน้ำ ลงในข้อความได้

วิธีที่ข้อมูลที่มองไม่เห็นถูกแนบกับอักขระหนึ่งตัว

  • ข้อความ Unicode แสดงเป็นลำดับของ โค้ดพอยต์ (codepoint) และโดยทั่วไปเขียนในรูปแบบ U+XXXX
  • ในอักขระละตินแบบง่าย โค้ดพอยต์กับอักขระที่มองเห็นบนหน้าจอจะสอดคล้องกันแบบ 1:1
    • ตัวอย่าง: U+0067 แทนอักขระ g
  • ในระบบตัวเขียนอื่น อักขระหนึ่งตัวบนหน้าจออาจประกอบด้วยหลายโค้ดพอยต์
    • ตัวอย่าง: ในเทวนาครี อักขระที่อ่านว่า คี แสดงด้วยคู่ลำดับของ U+0915 และ U+0940

ใช้ variation selector เหมือนที่เก็บข้อมูล

  • Unicode กำหนดโค้ดพอยต์ variation selector ไว้ 256 ตัว มีชื่อตั้งแต่ VS-1 ถึง VS-256
  • ตัว variation selector เองจะไม่แสดงบนหน้าจอ และใช้เพื่อเปลี่ยนรูปแบบการแสดงผลของอักขระก่อนหน้า
  • อักขระ Unicode ส่วนใหญ่ไม่มีรูปแบบแปรผันที่เชื่อมโยงอยู่ แต่เนื่องจาก Unicode มุ่งหวังความเข้ากันได้ในอนาคต โค้ดที่ประมวลผลแต่ไม่เข้าใจความหมายก็ควรเก็บ variation selector ไว้
    • แม้ใส่ U+FE01 (VS-2) ต่อท้าย U+0067 (g) บนหน้าจอก็ยังดูเหมือนตัวพิมพ์เล็ก g
    • เมื่อคัดลอก·วาง variation selector ก็จะติดไปด้วย
  • variation selector จำนวน 256 ตัวเป็นจำนวนที่แทน 1 ไบต์ ได้พอดี จึงซ่อนข้อมูล 1 ไบต์ไว้หลังโค้ดพอยต์ Unicode ใด ๆ ได้
  • ข้อกำหนด Unicode ไม่ได้ระบุรายละเอียดของลำดับที่มี variation selector หลายตัวต่อกัน และบอกเป็นนัยว่าควรถูกละเว้นระหว่างการเรนเดอร์
  • หากนำ variation selector หลายตัวมาเรียงต่อกัน ก็สามารถแทน ลำดับไบต์ใด ๆ ไว้หลังอักขระหนึ่งตัวได้

การเข้ารหัสไบต์เป็น variation selector

  • variation selector แบ่งเป็นสองช่วงโค้ดพอยต์
    • U+FE00 .. U+FE0F: 16 ตัวแรก
    • U+E0100 .. U+E01EF: อีก 240 ตัวที่เหลือ
  • กฎในการแปลงไบต์เป็น variation selector นั้นเรียบง่าย
    • ถ้าไบต์น้อยกว่า 16 ให้ใช้ 0xFE00 + byte
    • นอกนั้นให้ใช้ 0xE0100 + (byte - 16)
  • การเข้ารหัสจะใส่ อักขระฐาน (base character) หนึ่งตัวก่อน แล้วแปลงแต่ละไบต์เป็น variation selector และต่อท้ายเข้าไป
fn byte_to_variation_selector(byte: u8) -> char {
    if byte < 16 {
        char::from_u32(0xFE00 + byte as u32).unwrap()
    } else {
        char::from_u32(0xE0100 + (byte - 16) as u32).unwrap()
    }
}
fn encode(base: char, bytes: &[u8]) -> String {
    let mut result = String::new();
    result.push(base);
    for byte in bytes {
        result.push(byte_to_variation_selector(*byte));
    }
    result
}
  • เมื่อนำไบต์ที่แทน hello คือ [0x68, 0x65, 0x6c, 0x6c, 0x6f] ไปต่อท้าย 😊 ก็จะได้สตริงที่ภายนอกดูเหมือนอีโมติคอนธรรมดา
  • ในเอาต์พุตทั่วไปจะมองไม่เห็นอักขระที่ซ่อนอยู่ แต่ถ้าพิมพ์ด้วยฟอร์แมตดีบักของ Rust จะเห็นโค้ดพอยต์ที่ซ่อนอยู่ เช่น \u{e0158}
"😊\u{e0158}\u{e0155}\u{e015c}\u{e015c}\u{e015f}"

วิธีอ่านไบต์ที่ซ่อนอยู่กลับมา

  • การถอดรหัสทำโดยวนผ่านอักขระ แล้วแปลงโค้ดพอยต์ที่อยู่ในช่วง variation selector กลับเป็นไบต์
  • ช่วง U+FE00..U+FE0F กู้คืนด้วย variation_selector - 0xFE00
  • ช่วง U+E0100..U+E01EF กู้คืนด้วย variation_selector - 0xE0100 + 16
  • อักขระทั่วไปก่อนพบ variation selector ตัวแรกจะถือเป็น อักขระฐาน และถูกละเว้น
  • หากพบอักขระที่ไม่ใช่ variation selector และมีผลลัพธ์อยู่แล้ว ให้ยุติการถอดรหัส
fn variation_selector_to_byte(variation_selector: char) -> Option<u8> {
    let variation_selector = variation_selector as u32;
    if (0xFE00..=0xFE0F).contains(&variation_selector) {
        Some((variation_selector - 0xFE00) as u8)
    } else if (0xE0100..=0xE01EF).contains(&variation_selector) {
        Some((variation_selector - 0xE0100 + 16) as u8)
    } else {
        None
    }
}
  • เมื่อนำผลลัพธ์การเข้ารหัสเดียวกันมาถอดรหัสแล้วตีความเป็น UTF-8 จะได้ "hello"
  • อักขระฐานไม่จำเป็นต้องเป็นอีโมติคอน และการประมวลผล variation selector ก็เหมือนกันในอักขระทั่วไป
  • เหตุผลที่ใช้อีโมติคอนคือมันสนุกกว่า

ความเป็นไปได้ในการนำไปใช้ในทางที่ผิด

  • วิธีนี้เป็น การใช้ Unicode ในทางที่ผิด และไม่ควรใช้
  • ในผลลัพธ์ที่เรนเดอร์แล้ว ข้อมูลจะมองไม่เห็น ทำให้ผู้ดูแลหรือผู้รีวิวที่เป็นมนุษย์รู้ได้ยากว่ามีข้อมูลซ่อนอยู่
  • อาจถูกนำไปใช้ในทางที่ผิดเพื่อซ่อนข้อมูลให้ผ่านตัวกรองคอนเทนต์ของมนุษย์
  • ยังใช้กับ การทำลายน้ำ ในข้อความได้
    • หลังส่งข้อความให้หลายคน หากมีการรั่วไหล ก็สามารถติดตามผู้รับเดิมได้
    • ลำดับ variation selector จะรอดผ่านการคัดลอก·วางส่วนใหญ่
    • รองรับความหนาแน่นของข้อมูลตามต้องการ และถ้าต้องการก็ใส่ลายน้ำในทุกอักขระได้

LLM ประมวลผลข้อมูลที่ซ่อนอยู่ได้หรือไม่

  • หลังจากถูกโพสต์บน Hacker News ก็มีคำถามว่า LLM จัดการข้อมูลที่ซ่อนแบบนี้อย่างไร
  • โดยทั่วไป tokenizer ดูเหมือนจะ เก็บรักษา variation selector ไว้เป็นโทเค็น ดังนั้นในทางทฤษฎีโมเดลจึงเข้าถึงได้
  • OpenAI tokenizer เป็นเครื่องมือตรวจสอบที่ใช้ยืนยันเรื่องนี้ได้
  • โดยภาพรวม โมเดลดูเหมือนจะไม่ได้พยายามถอดรหัสโดยตรงจากภายใน
  • เมื่อใช้ร่วมกับ code interpreter บางโมเดลสามารถแกะข้อมูลที่ซ่อนอยู่ได้
    • Gemini 2 Flash ใช้ Codename Goose และ foreverVM แก้ตัวอย่างหนึ่งได้ใน 7 วินาที
    • ยังมี วิดีโอยาว ที่ Claude แก้ตัวอย่างหนึ่งได้ด้วย

1 ความคิดเห็น

 
GN⁺ 2025-02-13
ความคิดเห็นจาก Hacker News
  • ในแง่การใช้ Unicode ในทางที่ผิด นี่เป็นแค่ ยอดภูเขาน้ำแข็ง เท่านั้น เทคนิคคล้าย ๆ กันสามารถทำให้บัฟเฟอร์ล้นในหลายระบบที่รับสตริง Unicode ได้ และโดยทั่วไปมักจบลงที่ข้อผิดพลาดหรือแครช แต่ถ้าโชคดีอาจได้พฤติกรรมที่น่าสนใจทีเดียว
    สมัยก่อน Python 3 ตอนทำ penetration testing ผมเคยใช้แค่เครื่องหมายกำกับเสียงทำให้อักขระตัวเดียวขยายเป็นหลายไบต์จนทำให้บัฟเฟอร์ของเว็บเซิร์ฟเวอร์ฝั่งแบ็กเอนด์ล้น ตอนนั้นก็แค่แครชกับรีสตาร์ตอัตโนมัติ แต่ถ้าขุดลึกพอ ดูเหมือนจะใช้เป็น exploit กับระบบหรือซอฟต์แวร์บางอย่างได้

    • โจทย์ "encrypted runner" ใน Google CTF quals 2024 อิงไอเดียนี้
    • ใช่แล้ว ข้อความ Zalgo เป็นเคสทดสอบที่พบบ่อยสำหรับช่องกรอกข้อมูลบนเว็บไซต์ แต่ส่วนใหญ่ก็ไม่เกิดอะไรน่าสนใจนัก อย่างมากก็ไปแตะข้อยกเว้นเรื่องข้อจำกัดความยาวในฐานข้อมูล และโดยปกติโพรเซสก็ไม่ตายด้วยซ้ำ ข้อยกเว้นจบอยู่ในเธรดปัจจุบัน
      แม้แต่ในฟอร์มสมัยใหม่ ก็ทำให้เกิดอาการคล้ายกันได้เพียงแค่ปิด JavaScript และกรณีดีที่สุดคือเปิด debug ไว้ ทำให้มี stack trace หรือ query ถูกพิมพ์ออกมา และมีข้อมูลรั่วเล็กน้อย อีกความผิดพลาดที่พบบ่อยคือการนับความยาว \n กับ \r\n ในสตริงข้อความผิด โดย JavaScript มักนับ carriage return เป็น 1 ไบต์ แต่สเปก HTTP ต้องการ 2 ไบต์
      unescape(encodeURIComponent("ç")).length เป็นวิธีแบบคร่าว ๆ ในการเช็กความยาวเป็นไบต์อย่างรวดเร็วใน JavaScript และปัญหา \r\n ก็แก้ได้ด้วยการจัดระเบียบสตริงก่อนนับความยาว
    • ผมยังมือใหม่ อยากให้ช่วยอธิบายเพิ่มได้ไหมว่าสิ่งนี้เกิดขึ้นได้อย่างไร หรือทำอย่างไร? รู้สึกเหมือนเป็น ช่องโหว่ ที่น่าลองทดสอบ
  • อันนี้น่ารักดี แต่ก็ไม่ได้จำเป็นขนาดนั้น Unicode มีช่วงใหญ่ที่เรียกว่า PUA(private use area) ซึ่งโค้ดในช่วงนี้ไม่ได้แมปกับอักขระใด ๆ และจะไม่ถูกแมปในอนาคต จึงใช้สำหรับงานภายในหรือกำหนดเองโดยผู้ใช้
    ตัวอย่างเช่น ใน fish-shell เวลาพาร์สโทเคนเป็นสตริงอย่างปลอดภัย จะเปลี่ยนอักขระพิเศษที่ไม่ได้ escape ให้เหมือนเป็น code point Unicode อื่นในสตริง แต่ใส่ไว้ในพื้นที่ PUA แล้วค่อยดักจับใน pipeline ภายหลัง ไม่ควรปล่อยให้หลุดออกนอกขอบเขต API แต่เมื่อเจอแล้ว แนวทางที่แนะนำคือส่งผ่านไปตามเดิม และระบบกับไลบรารีส่วนใหญ่ก็ทำแบบนั้น นี่อาจเป็นช่องทางรั่วไหลที่ชัดเจนได้ เพราะนักพัฒนาปกติจำนวนมากไม่ค่อยรู้อะไรเกี่ยวกับ Unicode มากไปกว่า “ถ้าอยากหลีกเลี่ยงปัญหา internationalization ให้ใช้ Unicode เสมอ” จึงมักเปิดค้างไว้แบบนั้น

    • ลองทดสอบเองแล้ว อักขระ private use เรนเดอร์เป็นกล่องในสภาพแวดล้อมของผม (󰀀) ประเด็นสำคัญตรงนี้คือการเข้ารหัสให้มันถูกซ่อนตอนคัดลอก-วาง และถูกถือว่าเป็น “ส่วนหนึ่ง” ของอักขระอื่น
    • ความต่างคือโดยมากอักขระ PUA จะถูกเรนเดอร์ให้เห็นค่อนข้างชัดไม่ทางใดก็ทางหนึ่ง แต่ variation selector ไม่เป็นแบบนั้น
    • มีบริบทที่ขาดไป คือไอเดียนี้มาจากการสนทนารอบผลงานส่ง Open Heart Protocol
      https://news.ycombinator.com/item?id=42791378
      เพราะ API มีข้อจำกัดว่ารับเฉพาะอีโมจิ ความเป็นไปได้ในการนำไปใช้ในทางอาชญากรรมจึงถูกพูดถึงทันที สำหรับการใช้งานนั้นใช้ PUA ไม่ได้ ต้องเข้ารหัสไว้ในอีโมจิ
    • นี่ไม่ได้ใกล้เคียงกับการใช้ noncharacter ที่กำหนดไว้ มากกว่าพื้นที่ private-use เหรอ? PUA ยังถูกใช้กับการเข้ารหัสแบบไม่เป็นทางการของระบบอักษรที่ยังไม่เข้า Unicode หรืออย่างโลโก้ Apple ด้วย ถ้าใช้แบบนี้ก็กลัวว่าจะชนกัน
      noncharacter ที่กำหนดไว้มีทั้ง 0xFFFF, 0xFFFE หรือ code point สองตัวสุดท้ายของแต่ละ plane รวมถึงพื้นที่หนึ่งตรงกลาง Arabic Presentation Forms ด้วย ผมเข้าใจว่ามีการเพิ่มลงในรายการภายหลังเพื่อให้มี noncharacter มากขึ้นสำหรับให้ผู้คนใช้ในลักษณะนี้
    • พูดตรง ๆ ผมลองเอาคอมเมนต์นี้ไปวางในตัวถอดรหัสที่ให้มาแล้ว คิดว่าเป็นไปไม่ได้ที่จะพลาดประเด็นขนาดนี้ และต้องมีข้อความซ่อนอยู่ข้างในแน่ ๆ แต่ดูเหมือนว่าจะพลาดจริง ๆ หรือไม่ก็เว็บไซต์นี้กำลังลบสิ่งเหล่านั้นออก
      คุณไม่สามารถ watermark อักขระใด ๆ แบบมองไม่เห็นด้วยอักขระ PUA ที่ไม่ถูกรู้จักได้ เพราะมันไม่ได้ถูกถือว่าเป็น combining character แต่จะมีกรอบ placeholder ที่เรนเดอร์แยกออกมาแทน ตัวอย่าง: — แน่นอนว่าถ้าคุณใช้ private use area แบบส่วนตัวจริง ๆ มันก็อาจไม่ใช่กล่องก็ได้
  • ประมาณ 10 ปีก่อน ผมเคยใส่ U+202D LEFT-TO-RIGHT OVERRIDE ไว้กลางชื่อไฟล์บน Windows เพื่อแกล้งให้เพื่อนร่วมงานตกใจ funnypicturegnp.exe ดูเหมือน funnypictureexe.png
    ถ้าใส่ไอคอนกำหนดเองที่ดูเหมือนพรีวิวรูปภาพเข้าไปด้วย ก็ดูสมจริงทีเดียว

    • ผมเคยทำงานด้านตรวจจับฟิชชิง และนี่เป็นแพตเทิร์นที่ผู้โจมตีใช้กันบ่อย .exe ส่วนใหญ่จะถูกบล็อกอัตโนมัติ แต่ส่วนขยายอันตรายในยุคนี้มักเป็น .html และมักเปิดหน้าเข้าสู่ระบบปลอมด้วย window.location redirect ที่ถูกทำให้สับสน
      การใช้ RTL ในทางที่ผิดอย่าง cute-cat-lmth.png พบได้ค่อนข้างบ่อย แต่ตรวจจับง่ายมาก และอีเมลแบบนั้นจะถูกทำเครื่องหมายเป็นฟิชชิงทันที
    • เวอร์ชันซอร์สโค้ดของเทคนิคนี้คือ CVE-2021-42574 และมีเว็บไซต์ด้วย
      https://trojansource.codes/
      โดยพื้นฐานแล้วสามารถซ่อนโค้ดที่ดูเหมือนคอมเมนต์ แต่เมื่อคอมไพล์แล้วทำงานเป็นโค้ดได้ อย่างไรก็ตาม ผมจำได้ว่าสถานะ CVE ของมันเป็นที่ถกเถียง เพราะ text editor จำนวนมากทำให้คอมเมนต์น่าสงสัยแบบนี้มองเห็นได้แล้ว
    • ผมไม่รู้จักทริกเฉพาะนี้ แต่ดีใจที่นิสัย ระแวงเกินเหตุ ซึ่งผมทำมาหลายสิบปี คือเปิดไฟล์มีเดียที่อาจน่าสงสัยเสมอด้วย “คลิกขวา → Open with” ได้รับการยืนยันว่ามีเหตุผล
    • ผมเคยสร้าง ไฟล์ bat ที่ชื่อ guitar_tab.txt
  • ในกรณีใช้งานจริง Sanity ใช้ทริกนี้เพื่อเข้ารหัส Content Source Maps ไว้ในข้อความจริงที่ถูกส่งให้เว็บเพจใน “โหมดพรีวิว”0 ผู้แก้ไขสามารถคลิกข้อความหรือคอนเทนต์นั้น ๆ เพื่อไล่ย้อนกลับไปยังตำแหน่งต้นฉบับภายในโครงสร้างคอนเทนต์ที่ซับซ้อนได้อย่างง่ายดาย
    แต่ก็มีข้อเสียและข้อจำกัดอยู่ เช่น ต้องป้องกันไม่ให้มันถูกเติมเข้าไปในค่าที่ต้องถูก parse หรือใช้งานตามตัวอักษร เช่น วันที่·timestamp, URL, ID ถึงอย่างนั้นก็ยังเป็นทริกที่น่าสนใจมาก
    0 https://www.sanity.io/docs/stega
    [1] https://github.com/sanity-io/content-source-maps

  • ชอบไอเดียที่เอาสิ่งนี้ไปใช้กับ การทำ watermark ให้เอาต์พุตของ LLM มันลงตัวพอดี เพราะยังไง generator คุณภาพต่ำ 99% ที่แค่คัดลอก·วางก็หนีไม่พ้นโดนจับได้ และแทบไม่กระทบ use case หลักอื่น ๆ
    ก็สงสัยเหมือนกันว่าจะใส่ข้อมูลมากแค่ไหนต่อแต่ละตัวอักษรหรือ output token จะเป็น user ID, การอ้างอิง prompt, วันที่, หมายเลข token อะไรพวกนี้ไหม? และก็สงสัยด้วยว่าในเทอร์มินัลจะตีความอย่างไร เท่มากจริง ๆ

    • ไม่เข้าใจว่าทำไมทุกคนถึงคิดว่า AI watermarking จะใช้ได้ผล watermark แบบไหนก็ตามล้วนถูกลบออกได้ทันทีและง่ายดาย จึงไม่มีทางทำงานได้จริงอย่างถูกต้อง
      มาตรการป้องกัน AI ที่แท้จริงมีแค่การบังคับให้ทุกปฏิสัมพันธ์ของมนุษย์ต้องมีลายเซ็นคีย์ที่ยืนยันด้วยตัวตนจริงเท่านั้น ซึ่ง A: ไม่มีทางเกิดขึ้นเด็ดขาด และ B: อาจถูกนำไปใช้ในทางที่ผิดในประเทศที่มีรัฐบาลคอร์รัปชัน หรือประเทศที่มีรัฐบาลคอร์รัปชันซึ่งได้รับอิทธิพลจากอุตสาหกรรมเอกชนอย่างรุนแรง เช่น สหรัฐฯ
    • มี preprocessing ก่อนใส่เข้า dataset เยอะมาก จึงน่าประหลาดใจเหมือนกันถ้าลูกเล่นแบบนี้จะใช้ได้จริง
    • ในเทอร์มินัล Linux ส่วนใหญ่ สิ่งที่ส่งเข้าไปจะถูกส่งต่อไปเป็น byte sequence ตรง ๆ เทคนิคนี้เข้ากับ UTF-8 และไม่ได้ใช้ glyph เพิ่ม ดังนั้นในเทอร์มินัลที่เป็นไปตาม Unicode มันจะมองไม่เห็นด้วยตาเปล่า ผมลองทดสอบในบางตัวแล้ว
      แน่นอนว่าถ้าส่งประโยคเข้า xxd ก็จะเห็นมัน ข้อเสนอ PUA ในคอมเมนต์บนสุดตอนนี้ต่างจากอันนี้ตรงที่มองเห็นได้ทันที
      หลังจากทดสอบเพิ่ม พบว่าหลังจากวางลงในเทอร์มินัลแล้ว ใน xxd ข้อความผ่านไปได้โดยไม่เปลี่ยนรูปเลย แต่ถ้าเลือกในเทอร์มินัลแล้ววางกลับมาอีกครั้ง ใน X selection ของ mate terminal และ konsole จะเหลือเพียงไม่กี่คำแล้วถูกตัดไป ไม่แน่ใจว่าการถูกตัดเกิดจากเทอร์มินัลหรือ X กันแน่ ใน xterm ตัว e สุดท้ายถูกเปลี่ยนรูป และข้อความที่เลือกก็ถูกตัดมากกว่าเดิม
      ในไฟล์ ประโยคถูกบันทึกโดยไม่เปลี่ยนรูป ดังนั้นดูเหมือนว่าจะเป็นกรณีที่ข้อมูลบางส่วนหลุดหายตอนคัดลอกออกจากเทอร์มินัลมากกว่า ผมทดสอบโดย echo ประโยคลงไฟล์ทดสอบ แล้วเปิดในเบราว์เซอร์เพื่อคัดลอกข้อความมาตรวจสอบ
    • สำหรับ LLM watermarking ยังมีแนวทางอื่นที่ทนทานกว่าและตรวจจับได้ยากกว่านี้มาก โดยอาศัยข้อเท็จจริงที่ว่า LLM สร้าง การแจกแจงความน่าจะเป็น ที่ให้ความน่าจะเป็นกับ token ถัดไปที่เป็นไปได้แต่ละตัว แล้วสุ่ม sample จากในนั้นเพื่อสร้างเอาต์พุต
      ถ้าใส่ลูกเล่นลงไปในวิธี sampling ตอนสร้างข้อความ ภายหลังก็สามารถรัน LLM อีกครั้งแล้วสังเกตรูปแบบของเอาต์พุตเพื่อตรวจจับ fingerprint ได้ เช่น เลือก token ที่มีความน่าจะเป็นสูงและต่ำสลับกัน แน่นอนว่าการใช้งานจริงคงซับซ้อนกว่านี้มาก แต่ไอเดียไปในทิศทางนั้น
  • จุดที่น่าสนใจคือ screen reader สามารถตรวจพบ variation selector เหล่านี้ได้เมื่อเลื่อนทีละตัวอักษร ถ้าเลื่อนด้วยลูกศรบนตัวอย่าง มันจะอ่านว่า “Smiling face with smiling eyes”, “Symbol e zero one five five”, “Symbol e zero one five c”
    อย่างไรก็ตามขึ้นอยู่กับ speech synthesizer ที่ใช้ และถ้ากำลังอ่านข้อความในเอกสารตามปกติ ก็จะไม่รู้ว่ามีอักขระเหล่านั้นอยู่ จึงไม่ได้เป็นประโยชน์ใหญ่นักโดยรวม

    • ข้อความออนไลน์โดยทั่วไปถูกปนเปื้อนด้วยอักขระที่มองไม่เห็นแต่ฟังแล้วน่ารำคาญ ผมเลยใช้สคริปต์ใน screen reader ของตัวเองเพื่อลบ อักขระที่ไม่ใช่ ASCII ทั้งหมด
  • StegCloak0 ก็อยู่ในกลุ่มเดียวกัน และผลักไอเดียนี้ไปอีกขั้นด้วยการเข้ารหัส payload ที่ซ่อนไว้ด้วย AES-256-CTR เป็นทริกเล็ก ๆ ที่ใช้ได้ดีทีเดียว
    0 https://github.com/KuroLabs/stegcloak

    • ดูเหมือนว่าจะมีปลั๊กอิน Better Discord บางตัวที่ใช้วิธีนี้หรือวิธีคล้ายกัน สามารถส่ง ข้อความที่เข้ารหัสเต็มรูปแบบ ซึ่งสำหรับคนอื่นจะดูเหมือนไม่มีอะไรได้
      แต่ฝ่ายตรงข้ามต้องแชร์ secret รหัสผ่านกันก่อนถึงจะถอดรหัสได้
    • เคยลองทดสอบกับ TXT record ของ Cloudflare DNS แต่ Cloudflare ฉลาดพอที่จะ decode ตอนวางลงในฟิลด์ TXT ไปเลย
  • ชื่อเรื่องชวนเข้าใจผิดเล็กน้อย มีข้อความว่า “อักขระฐานไม่จำเป็นต้องเป็น emoji และการจัดการ variation selector ก็เหมือนกันกับอักขระทั่วไป แค่ใช้ emoji แล้วสนุกกว่าเท่านั้น”
    ถ้าใช้วิธีนี้กับ อักขระที่ไม่ใช่ emoji จะยิ่งแนบเนียนและยิ่งน่ารำคาญขึ้น

    • ดูเหมือนจะไม่ได้รำคาญถึงขนาดนั้น ตัวตรวจจับทำได้ไม่ยาก แค่แสดงผลเมื่อมี variant ไปติดกับอักขระที่จริง ๆ แล้วไม่มีรูปแปรผัน นอกจากนี้ยังดูเหมือนนำไปใช้เป็นลายเซ็นได้ด้วย
  • มากกว่าการทำ watermark เอาต์พุต LLM แบบง่าย ๆ สิ่งนี้ดูเหมือนอาจเป็นวิธีที่เรียบร้อยในการแพ็ก ข้อมูล logprobs ไปด้วย
    โดยพื้นฐานคือรวมข้อมูลความน่าจะเป็นของ token ทั้งหมดที่ถูกสร้างขึ้น เพื่อเพิ่มความโปร่งใสเล็กน้อยให้กับกระบวนการสร้าง ข้อมูลนี้มีอยู่ในสเปก OpenAI API ด้วย และเอนจินหลายตัวอย่าง llama.cpp ก็ให้ข้อมูลนี้เช่นกัน โดยปกติจะถูกแนบเป็นฟิลด์แยกต่างหาก แต่ก็มีวิธี visualize อย่าง mikupad0 ด้วย
    อาจจะเป็นไอเดียที่ไม่ดี แต่ก็เป็นแนวคิดที่คาใจอยู่ดี

  • เป็นเทคนิคที่เจ๋งมาก มีการสะท้อน ASCII และยังมี อักขระ Unicode Tag ซึ่งเป็นองค์ประกอบ UI ที่ไม่ค่อยพบเห็นบ่อยนัก โดยเฉพาะในเว็บแอป
    จุดเด่นของอักขระ Tag คือ LLM บางตัวจะตีความข้อความที่ซ่อนอยู่เป็น ASCII แล้วทำตามคำสั่ง และถึงขั้นสามารถเขียนออกมาได้โดยตรงด้วย
    https://embracethered.com/blog/posts/2024/hiding-and-finding...
    ยังมี proof of concept ของช่องโหว่จริงที่ Microsoft แก้ไขแล้วใน Copilot ด้วย
    https://embracethered.com/blog/posts/2024/m365-copilot-promp...