เราพบตัวระบุ Firefox ที่เสถียร ซึ่งสามารถเชื่อมโยงตัวตน Tor แบบส่วนตัวทั้งหมดของผู้ใช้ได้
(fingerprint.com)- เพียงแค่ดู ลำดับการส่งคืน ของ
indexedDB.databases()ก็สามารถสร้าง ตัวระบุที่เสถียร ซึ่งคงอยู่ตลอดอายุของโปรเซสได้บนเบราว์เซอร์ที่พัฒนาบน Firefox - ตัวระบุดังกล่าวถูกใช้ร่วมกันข้ามขอบเขต origin ทำให้แม้แต่เว็บไซต์ที่ไม่เกี่ยวข้องกันก็ยังสังเกตค่าเดียวกันได้ภายในรันไทม์ของเบราว์เซอร์เดียวกัน และสามารถนำไปใช้สำหรับ การติดตามข้าม origin ได้
- ใน Firefox Private Browsing ตัวระบุจะยังคงอยู่แม้ปิดหน้าต่างส่วนตัวทั้งหมดแล้ว ตราบใดที่โปรเซสยังไม่จบ และใน Tor Browser ก็ยังคงอยู่แม้หลังจากใช้ New Identity
- สาเหตุอยู่ที่การทำงานของ IndexedDB ใน Gecko ซึ่งแมปชื่อฐานข้อมูลส่วนตัวเป็นชื่อไฟล์ที่อิง UUID และเปิดเผยผลลัพธ์นั้นออกมาโดยไม่จัดเรียง
- Mozilla ได้ปล่อยการแก้ไขใน Firefox 150 และ ESR 140.10.0 แล้ว และการออกแบบที่ไม่เปิดเผยลำดับของสตอเรจภายในออกสู่ภายนอกมีความสำคัญต่อการคุ้มครองความเป็นส่วนตัว
ภาพรวมของช่องโหว่
- ใน เบราว์เซอร์ทั้งหมดที่พัฒนาบน Firefox สามารถดึง ตัวระบุที่คงอยู่ตลอดอายุของโปรเซส ได้จากลำดับของรายการที่
indexedDB.databases()ส่งคืน- หากเว็บไซต์สร้างฐานข้อมูล IndexedDB หลายตัวแล้วตรวจสอบลำดับที่ส่งคืน ก็สามารถสร้างตัวระบุที่เป็นเอกลักษณ์และกำหนดได้แน่นอนสำหรับโปรเซสเบราว์เซอร์ที่กำลังรันอยู่
- พฤติกรรมนี้ไม่ได้เกิดในขอบเขต origin แต่เกิดใน ขอบเขตโปรเซส ทำให้เว็บไซต์ที่ไม่เกี่ยวข้องกันสามารถสังเกตตัวระบุเดียวกันได้ภายในรันไทม์ของเบราว์เซอร์เดียวกัน
- ใน Firefox Private Browsing หากโปรเซส Firefox ยังทำงานอยู่ ตัวระบุจะยังคงอยู่แม้หลังจากปิดหน้าต่างส่วนตัวทั้งหมดแล้ว
- ใน Tor Browser ตัวระบุที่เสถียรนี้ยังคงอยู่แม้หลังจากใช้ New Identity ซึ่งจะล้างคุกกี้ ประวัติการเข้าชม และใช้วงจร Tor ใหม่
- สิ่งนี้ขัดกับความคาดหวังที่ว่ากิจกรรมการใช้งานหลังจากนั้นไม่ควรถูกเชื่อมโยงกับกิจกรรมก่อนหน้า และทำให้การรับประกันการไม่เชื่อมโยงที่ผู้ใช้พึ่งพาอ่อนแอลง
- มีการเปิดเผยอย่างรับผิดชอบต่อ Mozilla และ Tor Project
- Mozilla ได้ปล่อยการแก้ไขใน Firefox 150 และ ESR 140.10.0
- แพตช์กำลังถูกติดตามใน Mozilla Bug 2024220 และต้นเหตุอยู่ใน implementation ของ IndexedDB ใน Gecko จึงเกี่ยวข้องกับ Tor Browser และเบราว์เซอร์อื่นที่พัฒนาบน Firefox ด้วย
- หลักการของการแก้ไขนั้นเรียบง่าย
- เบราว์เซอร์ไม่ควรเปิดเผยลำดับของสตอเรจภายในระดับโปรเซสออกสู่ภายนอก
- หากทำการ normalize หรือจัดเรียงผลลัพธ์ก่อนส่งคืน ก็จะสามารถตัด entropy และป้องกันการนำไปใช้เป็นตัวระบุที่เสถียรได้
ทำไมเรื่องนี้จึงสำคัญ
- โหมดท่องเว็บแบบส่วนตัวและ เบราว์เซอร์ที่เน้นความเป็นส่วนตัว มีเป้าหมายเพื่อลดความสามารถในการระบุตัวผู้ใช้ในบริบทที่ต่างกัน
- ความคาดหวังทั่วไปข้อ 1: หากไม่มี shared storage หรือกลไกระบุตัวตนแบบชัดเจน เว็บไซต์ที่ไม่เกี่ยวข้องกันไม่ควรทราบได้ว่ากำลังโต้ตอบกับอินสแตนซ์เบราว์เซอร์เดียวกันหรือไม่
- ความคาดหวังทั่วไปข้อ 2: เมื่อเซสชันส่วนตัวสิ้นสุดลง ข้อมูลที่เชื่อมโยงกับเซสชันนั้นก็ควรหายไปด้วย
- พฤติกรรมนี้ทำลายทั้งสองความคาดหวัง
- เว็บไซต์สามารถอนุมานตัวระบุได้จากพฤติกรรมสตอเรจภายในของเบราว์เซอร์ โดยไม่ต้องใช้คุกกี้, localStorage หรือช่องทางข้ามเว็บไซต์แบบชัดเจน
- สามารถดึงสัญญาณระบุตัวตนที่มีความจุสูงได้จากลำดับชื่อฐานข้อมูลที่ API ส่งคืน
- ยังมีบทเรียนสำหรับนักพัฒนาด้วย
- ช่องโหว่ด้านความเป็นส่วนตัวไม่ได้เกิดจากการเข้าถึงข้อมูลระบุตัวตนโดยตรงเท่านั้น
- เมื่อรายละเอียดการทำงานภายในถูก เปิดเผยอย่างกำหนดแน่นอน ก็อาจทำให้ข้อมูลส่วนตัวรั่วไหลได้เช่นกัน
- ประเด็นสำคัญในมุมมองความปลอดภัยและผลิตภัณฑ์
- แม้ API จะดูไม่มีพิษภัย แต่หากมันรั่วไหล สถานะระดับโปรเซส ที่เสถียร ก็สามารถกลายเป็นเวกเตอร์สำหรับการติดตามข้ามเว็บไซต์ได้
IndexedDB และ indexedDB.databases()
- IndexedDB คือ API ของเบราว์เซอร์สำหรับจัดเก็บข้อมูลแบบมีโครงสร้างฝั่งไคลเอนต์
- เว็บแอปพลิเคชันใช้สำหรับรองรับออฟไลน์, แคช, สถานะเซสชัน และวัตถุประสงค์การจัดเก็บข้อมูลในเครื่องอื่น ๆ
- แต่ละ origin สามารถสร้างฐานข้อมูลที่มีชื่อได้หนึ่งรายการขึ้นไป และเก็บ object store กับข้อมูลขนาดใหญ่ได้
indexedDB.databases()จะส่งคืน metadata ของฐานข้อมูลที่มองเห็นได้จาก origin ปัจจุบัน- นักพัฒนาสามารถใช้เพื่อตรวจสอบฐานข้อมูลที่มีอยู่, ดีบักการใช้สตอเรจ และจัดการสถานะแอปพลิเคชัน
- ภายใต้ความคาดหวังด้านความเป็นส่วนตัวตามปกติ ลำดับการส่งคืนของ API นี้เองไม่ควรมีข้อมูลสำหรับการระบุตัวตน
- ควรเป็นการแสดง metadata ของฐานข้อมูลในรูปแบบที่เป็นกลางหรือผ่านการ normalize แล้ว
- ปัญหาจริงคือในเบราว์เซอร์ที่พัฒนาบน Firefox ลำดับการส่งคืนไม่ได้เป็นกลางเลย
indexedDB.databases() กลายเป็นตัวระบุที่เสถียรได้อย่างไร
- ใน Firefox Private Browsing
indexedDB.databases()จะส่งคืน metadata ตามลำดับที่ได้มาจาก โครงสร้างสตอเรจภายใน ไม่ใช่ตามลำดับการสร้างฐานข้อมูล- implementation ที่เกี่ยวข้องอยู่ใน
dom/indexedDB/ActorsParent.cpp
- implementation ที่เกี่ยวข้องอยู่ใน
- ใน Private Browsing จะไม่ใช้ชื่อฐานข้อมูลเป็นตัวระบุบนดิสก์โดยตรง
- แต่จะแมปเป็น ฐานชื่อไฟล์ที่อิง UUID ผ่าน global hash table
StorageDatabaseNameHashtable = nsTHashMap<nsString, nsString> - การแมปนี้ทำใน
GetDatabaseFilenameBase()ภายในOpenDatabaseOp::DoDatabaseWork()
- แต่จะแมปเป็น ฐานชื่อไฟล์ที่อิง UUID ผ่าน global hash table
- เมื่อ
aIsPrivateเป็น true ชื่อฐานข้อมูลที่เว็บไซต์กำหนดจะถูกแทนที่ด้วย UUID ที่สร้างขึ้น และบันทึกไว้ใน globalStorageDatabaseNameHashtable- ใช้เพียงสตริงชื่อฐานข้อมูลเป็นคีย์
- คงอยู่ตลอดอายุของ IndexedDB QuotaClient
- ใช้ร่วมกันระหว่างทุก origin
- จะถูกรีเซ็ตเมื่อรีสตาร์ต Firefox ทั้งหมดเท่านั้น
- ต่อมาเมื่อมีการเรียก
indexedDB.databases()Firefox จะรวบรวมชื่อไฟล์ฐานข้อมูลผ่านQuotaClient::GetDatabaseFilenames(...)ในGetDatabasesOp::DoDatabaseWork()- ชื่อฐานของฐานข้อมูลจะถูกใส่ลงใน
nsTHashSet - ไม่มีการจัดเรียงใด ๆ ก่อนเริ่มวนลูป
- ชื่อฐานของฐานข้อมูลจะถูกใส่ลงใน
- ลำดับผลลัพธ์สุดท้ายจึงถูกกำหนดโดยการไล่ traversal ตาม bucket layout ภายในของ hash set
- เนื่องจากการแมป UUID คงที่ตลอดอายุของโปรเซส Firefox ลำดับที่ส่งคืนจึงคงที่ตามฟังก์ชันที่กำหนดแน่นอนของค่า UUID ที่สร้างขึ้น, พฤติกรรมของฟังก์ชันแฮช, ความจุของ hash table และประวัติการแทรก
- ลำดับนี้คงอยู่ข้ามแท็บและหน้าต่างส่วนตัว และจะรีเซ็ตเมื่อรีสตาร์ต Firefox ทั้งหมดเท่านั้น
- ทั้งการแมป UUID และการไล่ traversal ของ hash set อยู่ใน ขอบเขตโปรเซส ไม่ใช่ขอบเขต origin
วิธีทำซ้ำ
- สามารถพิสูจน์พฤติกรรมได้ด้วย PoC ที่เรียบง่าย
- มีสอง origin ที่ต่างกันโฮสต์สคริปต์เดียวกัน
- แต่ละสคริปต์สร้างฐานข้อมูลจากชุดชื่อคงที่, เรียก
indexedDB.databases(), ดึงลำดับที่ส่งคืนออกมาและพิมพ์ผล
- บน Firefox Private Browsing และ Tor Browser รุ่นที่ได้รับผลกระทบ ทั้งสอง origin จะสังเกตเห็น permutation เดียวกัน ตลอดอายุของโปรเซสเบราว์เซอร์เดียวกัน
- เมื่อรีสตาร์ตเบราว์เซอร์ permutation จะเปลี่ยน
- มีตัวอย่างผลลัพธ์เชิงแนวคิดดังนี้
- ลำดับการสร้าง:
a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p - ลำดับที่ส่งคืน:
g,c,p,a,l,f,n,d,j,b,o,h,e,m,i,k
- ลำดับการสร้าง:
- ประเด็นสำคัญไม่ใช่ลำดับที่แน่นอนนั้นเอง
- มันแตกต่างจากลำดับการสร้างเดิม
- origin ที่ไม่เกี่ยวข้องกันกลับได้ลำดับเดียวกัน
- มันยังคงอยู่แม้รีเฟรช, เปิดหน้าต่างส่วนตัวใหม่ หรือปิดหน้าต่างส่วนตัวทั้งหมดแล้ว
- จะสร้างลำดับใหม่เฉพาะเมื่อรีสตาร์ตเบราว์เซอร์ทั้งหมดเท่านั้น
- จึงยืนยันได้โดยตรงว่ามีคุณสมบัติที่ไม่พึงประสงค์ในมุมมองความเป็นส่วนตัว
ผลกระทบต่อความเป็นส่วนตัว
- ช่องโหว่นี้ทำให้สามารถติดตามได้ทั้งแบบ cross-origin และ same-origin ภายในรันไทม์ของเบราว์เซอร์เดียวกัน
-
ผลกระทบแบบ cross-origin
- เว็บไซต์ที่ไม่เกี่ยวข้องกันสามารถอนุมานตัวระบุเดียวกันอย่างอิสระ และสรุปได้ว่ากำลังโต้ตอบกับโปรเซส Firefox หรือ Tor Browser ที่กำลังรันอยู่ตัวเดียวกัน
- ทำให้เชื่อมโยงกิจกรรมข้ามโดเมนได้แม้ไม่มีคุกกี้หรือ shared storage แบบอื่น
-
ผลกระทบแบบ same-origin
- ใน Firefox Private Browsing หากโปรเซส Firefox ยังทำงานอยู่ ตัวระบุจะยังคงอยู่แม้หลังจากปิดหน้าต่างส่วนตัวทั้งหมดแล้ว
- เว็บไซต์สามารถจดจำการกลับมาเยี่ยมชมภายหลังซึ่งภายนอกดูเหมือนเป็นเซสชันส่วนตัวใหม่ได้อีกครั้ง
- ใน Tor Browser ตัวระบุที่เสถียรนี้ทำให้การแยกด้วย New Identity ภายในโปรเซสเบราว์เซอร์ที่กำลังรันอยู่ แทบไร้ผลในทางปฏิบัติ
- เปิดให้เกิดการเชื่อมโยงระหว่างเซสชันที่ควรถูกแยกออกจากกันโดยสิ้นเชิง
-
ทำไมจึงร้ายแรงเป็นพิเศษใน Tor Browser
- Tor Browser ถูกออกแบบมาเพื่อลดความเป็นไปได้ในการเชื่อมโยงข้ามเว็บไซต์ และลดการระบุตัวตนในระดับอินสแตนซ์เบราว์เซอร์ให้น้อยที่สุด
- ตัวระบุที่เสถียรและคงอยู่ตลอดอายุของโปรเซสขัดแย้งกับเป้าหมายการออกแบบนี้โดยตรง
- แม้มันจะอยู่รอดเพียงจนกว่าจะรีสตาร์ตโปรเซสทั้งหมด ก็ยังมากพอที่จะทำให้การไม่เชื่อมโยงอ่อนแอลงระหว่างการใช้งานจริงที่ต่อเนื่อง
Entropy และความจุของ fingerprinting
- สัญญาณนี้ไม่เพียงเสถียร แต่ยัง มีความจุสูง ด้วย
- หากเว็บไซต์ควบคุมชื่อฐานข้อมูลได้
Nชื่อ จำนวน permutation ที่สังเกตได้คือN! - entropy เชิงทฤษฎีคือ
log2(N!)
- หากเว็บไซต์ควบคุมชื่อฐานข้อมูลได้
- หากมีชื่อที่ควบคุมได้ 16 ชื่อ พื้นที่เชิงทฤษฎีจะอยู่ที่ประมาณ 44 บิต
- มากพอสำหรับแยกแยะอินสแตนซ์เบราว์เซอร์ที่ทำงานพร้อมกันในสภาพแวดล้อมจริง
- เนื่องจากพฤติกรรมของ hash table ภายใน จำนวน permutation ที่เข้าถึงได้จริงอาจต่ำกว่านี้เล็กน้อย
- อย่างไรก็ตาม ประเด็นด้านความปลอดภัยไม่ได้เปลี่ยนไป
- ลำดับที่ถูกเปิดเผยยังคงให้ entropy มากพอที่จะทำหน้าที่เป็นตัวระบุที่มีพลังได้
วิธีแก้ไข
- การแก้ไขที่ถูกต้องคือต้องหยุด การเปิดเผย entropy ที่ได้มาจาก layout ของสตอเรจภายใน
- แนวทางบรรเทาที่สะอาดที่สุดคือส่งคืนผลลัพธ์ใน ลำดับมาตรฐาน เช่น การจัดเรียงตามลำดับพจนานุกรม
- วิธีนี้ยังคงประโยชน์ของ API สำหรับนักพัฒนาไว้ พร้อมทั้งลบสัญญาณ fingerprinting ออก
- การสุ่มผลลัพธ์ทุกครั้งที่เรียกก็สามารถซ่อนลำดับที่เสถียรได้เช่นกัน
- แต่การจัดเรียงนั้นเรียบง่ายกว่า, คาดการณ์ได้มากกว่า และเป็นทางเลือกที่นักพัฒนาเข้าใจได้ง่ายกว่า
- มีการระบุเงื่อนไขของการแก้ไขที่เหมาะสมในมุมมองวิศวกรรมความปลอดภัย
- ความซับซ้อนเชิงแนวคิดต่ำ
- ความเสี่ยงด้านความเข้ากันได้ต่ำที่สุด
- กำจัดการรั่วไหลของความเป็นส่วนตัวได้โดยตรง
การเปิดเผยอย่างรับผิดชอบ
- มีการเปิดเผยอย่างรับผิดชอบต่อ Mozilla และ Tor Project
- Mozilla ได้ปล่อยการแก้ไขใน Firefox 150 และ ESR 140.10.0
- แพตช์กำลังถูกติดตามใน Mozilla Bug 2024220
- รากของพฤติกรรมนี้อยู่ใน implementation ของ IndexedDB ใน Gecko
- ดังนั้นเบราว์เซอร์ลูกที่พัฒนาบน Gecko รวมถึง Tor Browser ก็อยู่ในขอบเขตผลกระทบด้วย หากไม่มีมาตรการบรรเทาเฉพาะของตนเอง
การออกแบบที่เน้นความเป็นส่วนตัว
- รายละเอียด implementation เล็ก ๆ ก็อาจนำไปสู่ปัญหาความเป็นส่วนตัวที่มีนัยสำคัญได้
- เว็บไซต์ที่ไม่เกี่ยวข้องกันสามารถเชื่อมโยงกิจกรรมข้าม origin ได้ตลอดรันไทม์ของเบราว์เซอร์เดียวกัน
- ตัวระบุมีอายุยาวนานกว่าที่ผู้ใช้คาดหวัง ทำให้ขอบเขตของเซสชันส่วนตัวอ่อนแอลง
- ด้านบวกคือการแก้ไขนั้นเรียบง่ายและมีประสิทธิภาพ
- หาก normalize ผลลัพธ์ก่อนส่งคืน ก็จะตัดแหล่ง entropy นี้ออกได้
- และสามารถฟื้นฟูขอบเขตความเป็นส่วนตัวตามที่คาดหวังได้
- นี่เป็นช่องโหว่ประเภทที่มองข้ามได้ง่ายแต่มีผลกระทบสูง และควรค่าแก่ความระมัดระวังอย่างมากเมื่อสร้าง ฟีเจอร์ที่ไวต่อความเป็นส่วนตัว
ยังไม่มีความคิดเห็น