• เพียงแค่ดู ลำดับการส่งคืน ของ 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
  • ใน Private Browsing จะไม่ใช้ชื่อฐานข้อมูลเป็นตัวระบุบนดิสก์โดยตรง
    • แต่จะแมปเป็น ฐานชื่อไฟล์ที่อิง UUID ผ่าน global hash table StorageDatabaseNameHashtable = nsTHashMap<nsString, nsString>
    • การแมปนี้ทำใน GetDatabaseFilenameBase() ภายใน OpenDatabaseOp::DoDatabaseWork()
  • เมื่อ aIsPrivate เป็น true ชื่อฐานข้อมูลที่เว็บไซต์กำหนดจะถูกแทนที่ด้วย UUID ที่สร้างขึ้น และบันทึกไว้ใน global StorageDatabaseNameHashtable
    • ใช้เพียงสตริงชื่อฐานข้อมูลเป็นคีย์
    • คงอยู่ตลอดอายุของ 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 นี้ออกได้
    • และสามารถฟื้นฟูขอบเขตความเป็นส่วนตัวตามที่คาดหวังได้
  • นี่เป็นช่องโหว่ประเภทที่มองข้ามได้ง่ายแต่มีผลกระทบสูง และควรค่าแก่ความระมัดระวังอย่างมากเมื่อสร้าง ฟีเจอร์ที่ไวต่อความเป็นส่วนตัว

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น