Canvas Fingerprinting เหนือระดับพื้นฐาน: ทำไม Chrome สองตัวที่เหมือนกันจึงเรนเดอร์พิกเซลต่างกัน
อุปกรณ์สองเครื่องที่ใช้ Chrome, OS และ GPU เหมือนกัน ยังเรนเดอร์พิกเซลบน canvas ต่างกัน ทำไมกระบวนการเรนเดอร์จึงไม่แน่นอน ทำไมการฉีด noise เพื่อความเป็นส่วนตัวจึงย้อนแย้ง และ canvas อยู่ตรงไหนในระบบตรวจจับยุคใหม่
Canvas fingerprinting ถูกใช้งานในระดับ production มาตั้งแต่ปี 2012 แนวคิดนั้นตรงไปตรงมา คือ ให้เบราว์เซอร์เรนเดอร์เนื้อหาแบบ 2D แล้วอ่านค่าพิกเซลกลับมา จากนั้นนำไป hash อุปกรณ์ต่างกันจะให้พิกเซลที่แตกต่างกันเล็กน้อยแม้จะได้รับคำสั่งชุดเดียวกัน และความแตกต่างเหล่านั้นเสถียรพอที่จะระบุตัวอุปกรณ์ข้ามเซสชันได้
เรื่องนี้เป็นที่รู้กันดี สิ่งที่เข้าใจกันน้อยกว่าคือ ทำไมเทคนิคนี้จึงทำงานได้ — ทำไมการตั้งค่าซอฟต์แวร์ที่เหมือนกันบนฮาร์ดแวร์ที่เหมือนกัน ยังให้ผลลัพธ์พิกเซลที่ต่างกัน คำตอบอยู่ที่จุดตัดกันระหว่างไดรเวอร์ GPU, การเรนเดอร์ฟอนต์ และเลขคณิตแบบทศนิยม (floating-point)
กระบวนการเรนเดอร์ไม่ได้เป็นแบบ deterministic
เมื่อ JavaScript เรียก context.fillText("Cwm fjord bank glyphs vext quiz", 4, 45) Chrome ไม่ได้เรนเดอร์พิกเซลด้วยตัวเอง แต่มันสร้างคำสั่งให้กับ Skia — ไลบรารีกราฟิก 2D — ซึ่งจะออกคำสั่งวาดไปยัง backend ที่เร่งความเร็วด้วย GPU อีกทอดหนึ่ง (โดยทั่วไปคือ ANGLE บน Windows, Metal บน macOS หรือ software fallback ในคอนเทนเนอร์)
ในแต่ละขั้นตอน ความแตกต่างเล็ก ๆ ในการ implement จะสะสมกันขึ้นเรื่อย ๆ
-
ระบบย่อยด้านฟอนต์ของ Skia จะเลือกตระกูลฟอนต์ (font family) หากไม่มี Arial ติดตั้งอยู่ มันจะ fall back — และตัวสำรองก็แตกต่างกันไปตามฟอนต์ที่ติดตั้งในระบบ
-
ฟอนต์ที่เลือกจะถูกเรนเดอร์ที่ขนาดที่ร้องขอ โดยใช้ FreeType (บน Linux), DirectWrite (บน Windows) หรือ Core Text (บน macOS) แต่ละไลบรารีมีอัลกอริทึม hinting ที่ต่างกัน
-
มีการใช้การจัดตำแหน่งระดับ subpixel นี่คือจุดที่ฟอนต์เดียวกันเริ่มให้พิกเซลที่ต่างกัน — อัลกอริทึมปัดตำแหน่งไปยังค่า offset ของพิกเซลแบบเศษส่วน และกฎการปัดเศษก็ต่างกันไปในแต่ละแพลตฟอร์ม
-
ข้อความที่ผ่านการ rasterize จะถูกนำไป composite ลงบน canvas การ blend ทำผ่าน GPU shader บนระบบที่เร่งความเร็วด้วยฮาร์ดแวร์ และทำด้วยโค้ด CPU ในโหมด fallback
แต่ละขั้นตอนเหล่านี้สามารถสร้างความแตกต่างในค่าพิกเซลได้หนึ่งถึงสองค่า แยกกันแล้วมองไม่เห็น แต่รวมกันแล้วเป็นเอกลักษณ์
เวอร์ชันไดรเวอร์ GPU มีอิทธิพลเหนือ fingerprint
เครื่อง Windows 11 สองเครื่องที่ใช้ชิป Intel UHD Graphics 620 สามารถให้เอาต์พุต canvas ที่ต่างกันได้ หากไดรเวอร์ GPU ของทั้งสองต่างกัน Intel ปล่อยอัปเดตไดรเวอร์หลายครั้งต่อปี และแต่ละอัปเดตสามารถเปลี่ยนการกรองระดับ subpixel, การแก้ค่า gamma และการทำ anti-aliasing ของข้อความได้
นี่หมายความว่าความเสถียรของ canvas fingerprint ถูกจำกัดด้วยความถี่ในการอัปเดตไดรเวอร์ ผู้ใช้ที่อัปเกรดไดรเวอร์กราฟิกของตน — ซึ่งมักเกิดขึ้นแบบเงียบ ๆ ผ่าน Windows Update — จะเห็น canvas fingerprint ของตนเปลี่ยนไป
ระบบตรวจจับรับมือกับเรื่องนี้ด้วยการมอง canvas fingerprint เป็นเพียงหนึ่งในสัญญาณจำนวนมาก ไม่ใช่ตัวระบุตัวตนแบบเดี่ยว ๆ เมื่อมันเปลี่ยนไปแต่สัญญาณอื่นยังคงเสถียร (TLS fingerprint, WebGL renderer, timezone, ฟอนต์ที่ติดตั้ง) ก็ยังจดจำผู้เข้าชมได้อยู่ดี
การเรนเดอร์ emoji คือขุมทอง
องค์ประกอบที่ระบุตัวตนได้มากที่สุดของ canvas fingerprint มักไม่ใช่ข้อความหรือรูปทรง แต่เป็น emoji การเรนเดอร์ emoji ขึ้นอยู่กับฟอนต์ emoji ที่มาพร้อมกับ OS — Segoe UI Emoji บน Windows, Apple Color Emoji บน macOS, Noto Color Emoji บน Android
แม้แต่ภายใน OS เดียวกัน การเรนเดอร์ emoji ก็เปลี่ยนไปในแต่ละเวอร์ชัน การเรนเดอร์ emoji รูปสายรุ้งของ Windows 10 ต่างจากของ Windows 11 หน้ายิ้มของ iOS 16 มี anti-aliasing ต่างจากของ iOS 17
สคริปต์ canvas fingerprinting สมัยใหม่จงใจเรนเดอร์ลำดับของ emoji ซึ่งรวมถึงตัวที่เพิ่งเพิ่มเข้าไปใน Unicode ผู้เข้าชมที่อ้างว่ารัน iPhone Safari แต่ไม่สามารถเรนเดอร์ emoji ที่เพิ่งเพิ่มใน iOS 17 ได้ แสดงว่ากำลังรัน iOS เวอร์ชันเก่ากว่า — หรือกำลังโกหกเรื่องแพลตฟอร์ม
เฟรมเวิร์ก automation สร้าง fingerprint ที่มีลักษณะเฉพาะตัว
Headless Chrome ที่รันในคอนเทนเนอร์ Docker โดยไม่มี GPU จะใช้ SwiftShader ในการเรนเดอร์ SwiftShader ให้เอาต์พุต canvas ที่สอดคล้องกันภายในตัวเอง แต่แตกต่างจาก Chrome ที่เร่งความเร็วด้วยฮาร์ดแวร์ตัวใด ๆ
ลายเซ็นนี้สังเกตได้ชัด คือ anti-aliasing ที่สะอาดผิดปกติ, ค่าสีเฉพาะในบริเวณ gradient และขอบข้อความที่ไม่ตรงกับไดรเวอร์ GPU ตัวใดที่รู้จัก canvas fingerprint ที่ตรงกับ SwiftShader ใน Chrome 124 ในคอนเทนเนอร์ Linux เกือบจะเป็น automation อย่างแน่นอน — ไม่มีผู้ใช้จริงคนไหนรันเบราว์เซอร์แบบนี้
Puppeteer-extra-stealth พยายามปลอมเอาต์พุต canvas ด้วยการดักจับ toDataURL() แล้วคืนค่าข้อมูลที่ถูกแก้ไข แต่การแก้ไขนั้นมักสร้าง artifact ของตัวเองขึ้นมา — รูปแบบของ noise ที่ซ้ำ ๆ ข้ามเซสชัน ซึ่งย้อนแย้งตรงที่ทำให้ความพยายาม anti-fingerprinting ถูกตรวจจับได้ง่ายกว่าการไม่ทำอะไรเลยเสียอีก
ทำไมการฉีด noise จึงไม่ค่อยได้ผล
เบราว์เซอร์ที่เน้นความเป็นส่วนตัวอย่าง Brave ฉีด noise เข้าไปในเอาต์พุต canvas เพื่อป้องกันการติดตาม แนวคิดคือเพิ่มการแปรผันแบบสุ่มเล็ก ๆ เข้าไปในค่าพิกเซล เพื่อให้อุปกรณ์เดียวกันสร้าง fingerprint ที่ต่างกันในแต่ละเซสชัน
ในทางปฏิบัติ การฉีด noise มีปัญหาสามข้อ
1. ตัว noise เองก็คือ fingerprint ผู้เข้าชมที่เอาต์พุต canvas เปลี่ยนไปทุกครั้งที่โหลด แต่สัญญาณอื่น ๆ ยังคงเหมือนเดิม จะถูกจดจำได้ว่าเป็นเบราว์เซอร์ที่ฉีด noise ซึ่งตัวมันเองก็ระบุตัวตนได้
2. อัลกอริทึม noise นั้นเสถียร การ implement ของ Brave ให้รูปแบบ noise ที่เฉพาะเจาะจง ซึ่งไม่ตรงกับความแปรปรวนตามธรรมชาติของการเรนเดอร์ GPU ระบบตรวจจับสามารถแยกแยะได้ว่าเอาต์พุต canvas ตัวไหนดูเหมือนถูกฉีด noise เทียบกับตัวที่ดูเป็นธรรมชาติ
3. ระบบที่ซับซ้อนนำหลายตัวอย่างมารวมกัน การเรนเดอร์หลายครั้งบนหน้าเดียวกัน หรือข้ามเซสชัน สามารถนำมาเฉลี่ยกันได้ ถ้า noise มีขนาดเล็ก ค่าเฉลี่ยจะลู่เข้าหา fingerprint แบบ deterministic ที่อยู่เบื้องหลัง
Canvas fingerprinting เก็บข้อมูลอะไรได้บ้าง
นอกเหนือจากเอาต์พุตด้านภาพที่เห็นได้ชัด canvas fingerprinting ยังสามารถดึงคุณสมบัติของอุปกรณ์หลายอย่างออกมาเป็นผลพลอยได้
เวลาในการเรนเดอร์ — ระยะเวลาที่ canvas ใช้ในการเรนเดอร์เผยให้เห็นความสามารถของ GPU เดสก์ท็อป 4K ที่มี GPU แยกเรนเดอร์ได้เร็วกว่าแล็ปท็อประดับกลาง
เมตริกของข้อความ — measureText() คืนค่าความกว้างพิกเซลที่แม่นยำซึ่งแปรผันตามการเรนเดอร์ฟอนต์ ความกว้างของข้อความสามารถใช้เป็น fingerprint ได้แม้ไม่ต้องเรนเดอร์เลย
การรองรับ composite operation — เบราว์เซอร์หรือการตั้งค่า GPU บางแบบรองรับโหมด composite แบบขยาย การทดสอบการรองรับเผยให้เห็นขอบเขตความสามารถ
เอฟเฟกต์ฟิลเตอร์ — ฟิลเตอร์สไตล์ CSS ที่ใช้กับ canvas ให้เอาต์พุตต่างกันในแต่ละ GPU โดยเฉพาะฟิลเตอร์ blur และ drop-shadow
Canvas fingerprinting อยู่ตรงไหนในระบบตรวจจับ
หากอยู่โดดเดี่ยว canvas fingerprinting ไม่ใช่ตัวระบุตัวตนที่แข็งแกร่ง ไดรเวอร์เปลี่ยน ผู้ใช้เปลี่ยนเบราว์เซอร์ การฉีด noise ก็พบได้ทั่วไปในกลุ่มผู้ใช้ที่ใส่ใจความเป็นส่วนตัว
จุดแข็งที่แท้จริงของมันคือการเป็นตัวตรวจสอบข้าม (cross-check) เมื่อเซสชันหนึ่งอ้างว่าเป็นผู้ใช้ที่กลับมาซ้ำ โดยอิงจากคุกกี้หรือการล็อกอินบัญชี canvas fingerprint จะทำหน้าที่เป็นสัญญาณที่สอง — การตรงกับ fingerprint ในอดีตยืนยันการอ้างนั้น ส่วนการไม่ตรงกันก็เป็นการยกธงเตือนถึงการยึดบัญชี (account takeover) หรือการ hijack เซสชัน
สำหรับการตรวจจับบอต canvas fingerprinting โดดเด่นในการจับ automation ที่รันในคอนเทนเนอร์ ผู้ใช้จริงมี GPU จริง GPU จริงให้ลายเซ็นการเรนเดอร์ที่จดจำได้ อะไรก็ตามที่เรนเดอร์เหมือน SwiftShader หรือเหมือน canvas ที่ปลอมมาอย่างห่วย ๆ ก็จะตกอยู่ในกลุ่มเกือบจะเป็นบอตอย่างแน่นอน
เทคนิคจากปี 2012 ยังใช้ได้ในปี 2026 ด้วยเหตุผลเดียวกัน คือ การเรนเดอร์เป็นแบบ deterministic ต่อสภาพแวดล้อม แต่แทบเป็นไปไม่ได้ที่จะปลอมได้อย่างสมบูรณ์ ทุกความพยายามที่จะซ่อนตัวล้วนทิ้งร่องรอยของความพยายามนั้นเอาไว้เสมอ