องค์กรมักจะฝังหน้าลงชื่อเข้าใช้ภายใน iframe เพื่อให้การตรวจสอบสิทธิ์ในบริบทเป็นไปอย่างราบรื่นในหลายโดเมน อย่างไรก็ตาม การโหลดบริบทการตรวจสอบสิทธิ์ภายในเฟรมของบุคคลที่สามจะทำให้ผู้ใช้เสี่ยงต่อภัยคุกคามร้ายแรง เช่น การคลิกแจ็กกิ้ง (การเปลี่ยนเส้นทาง UI) และการสร้างข้อมูลเข้าสู่ระบบที่ไม่ได้รับอนุญาต เบราว์เซอร์จะปิดใช้ WebAuthn ใน iframe แบบข้ามต้นทางโดยค่าเริ่มต้นเพื่อลดความเสี่ยงเหล่านี้ การยกเลิกการจำกัดนี้อย่างปลอดภัยต้องใช้โปรโตคอลการป้องกันแบบหลายชั้นที่ใช้งานอยู่
ระบุโมเดลภัยคุกคาม
ก่อนเปิดใช้พาสคีย์ (WebAuthn) ภายในเฟรมย่อย โปรดทำความเข้าใจสถานการณ์การละเมิดที่คุณป้องกัน
- การติดตามโดยใช้การแทรก iframe ที่ซ่อนอยู่: ผู้โจมตีจะทริกเกอร์ข้อความแจ้ง WebAuthn จากโดเมนของตนเองโดยใช้โฆษณาหรือวิดเจ็ตในเว็บไซต์ที่เชื่อถือได้ หลอกให้ผู้ใช้ให้สิทธิ์พาสคีย์โดยไม่เห็นบริบท การดำเนินการนี้ จะลิงก์ข้อมูลประจำตัวของผู้ใช้กับบัญชีที่ผู้โจมตีควบคุมเพื่อรวบรวมข้อมูล
- การซ้อนทับภาพและการคลิกแจ็กกิ้ง (การเปลี่ยนเส้นทาง UI): หน้าหลักที่เป็นอันตรายจะแสดง iframe การตรวจสอบสิทธิ์แบบมองไม่เห็นโดยใช้ CSS มาตรฐานและซ้อนทับองค์ประกอบ UI ปลอมเพื่อขโมยการคลิกที่ทริกเกอร์โฟลว์การตรวจสอบสิทธิ์ ซึ่งอาจส่งผลให้เกิดการลักลอบใช้เซสชันหรือการบังคับให้ดำเนินการที่ไม่ได้รับอนุญาต หากผู้ใช้ดำเนินการตามพรอมต์โดยไม่ตั้งใจ
หากต้องการรับมือกับภัยคุกคามเหล่านี้ ให้ทำตามแนวทางปฏิบัติแนะนำต่อไปนี้
สำหรับเอกสารระดับบนสุด (เฟรมบนสุด)
สำหรับเอกสารที่ฝัง (iframe) ให้ทำดังนี้
- เปิดใช้คุกกี้ของบุคคลที่สามที่แบ่งพาร์ติชัน
- ปกป้องปลายทางด้วยนโยบายรักษาความปลอดภัยเนื้อหา
- เชื่อถือ แต่ตรวจสอบฝั่งเซิร์ฟเวอร์
สำหรับทั้ง 2 เอกสาร ให้ทำดังนี้
เปิดใช้การมอบสิทธิ์โดยใช้นโยบายสิทธิ์
โดยค่าเริ่มต้น เบราว์เซอร์จะบล็อกการเข้าถึง WebAuthn ใน iframe แบบข้ามต้นทาง นโยบาย สิทธิ์ เป็นกลไกแพลตฟอร์มเว็บแบบครบวงจรที่ช่วยให้เอกสารระดับบนสุดมอบสิทธิ์ ความสามารถอันทรงพลังเหล่านี้อย่างชัดเจนให้กับต้นทางของบุคคลที่สามที่เฉพาะเจาะจงและเชื่อถือได้
โทเค็นฟีเจอร์
WebAuthn ใช้โทเค็น 2 รายการที่แตกต่างกัน ได้แก่
publickey-credentials-get: ให้สิทธิ์สำหรับโฟลว์การลงชื่อเข้าใช้ด้วยพาสคีย์ (navigator.credentials.get())publickey-credentials-create: ให้สิทธิ์สำหรับขั้นตอนการลงทะเบียนพาสคีย์ (navigator.credentials.create())
ข้อกำหนดสำหรับการเปิดใช้
การเปิดใช้ความสามารถเหล่านี้ต้องมีการจัดแนวทั้งในคำตอบของเซิร์ฟเวอร์หลัก และมาร์กอัปฝั่งไคลเอ็นต์
- ส่วนหัวการตอบกลับ HTTP ของนโยบายสิทธิ์ (เว็บไซต์เซิร์ฟเวอร์หลัก): หน้าหลักต้องประกาศต้นทางที่อนุญาตในส่วนหัวการตอบกลับ HTTP โดยใช้ไวยากรณ์Structured Fields
Permissions-Policy: publickey-credentials-get=(self "https://embedded-auth.example.com")
นโยบายสิทธิ์: ความเข้ากันได้ของ publickey-credentials-get:
นโยบายสิทธิ์: ความเข้ากันได้ของ publickey-credentials-create:
- แอตทริบิวต์
allowของ HTML: ในมาร์กอัป HTML องค์ประกอบ<iframe>ต้องประกาศด้วยว่าเปิดใช้ฟีเจอร์นี้
<iframe src="https://embedded-auth.example.com?nonce=deadbeef12345678&client=https%3A%2F%2Fembedded-auth.example.com" allow="publickey-credentials-get"></iframe>
ความเข้ากันได้ของ iframe allow="publickey-credentials-get":
Browser Support
ความเข้ากันได้ของ iframe allow="publickey-credentials-create":
Browser Support
เปิดใช้คุกกี้ของบุคคลที่สามที่แบ่งพาร์ติชัน
ต้องสร้างและรักษาเซสชันไว้ภายใน iframe แบบข้ามต้นทางที่ฝังไว้เพื่อให้มั่นใจว่าขั้นตอนการตรวจสอบสิทธิ์จะเชื่อถือได้ เนื่องจากเบราว์เซอร์สมัยใหม่ เปลี่ยนไปใช้การจำกัดคุกกี้ของบุคคลที่สามแบบเข้มงวด กลไกการคงอยู่มาตรฐาน จึงมักจะถูกบล็อกโดยค่าเริ่มต้น และอาจต้องเรียกใช้ Storage Access API เพื่อ รับสิทธิ์เข้าถึง
หากต้องการลดอุปสรรคเหล่านี้ ให้กำหนดค่าคุกกี้เซสชันด้วยแอตทริบิวต์ SameSite:
None, Secure และ Partitioned กลไกแพลตฟอร์มแบบรวมนี้
ช่วยให้มั่นใจได้ว่าสถานะจะยังคงอยู่ใน iframe ขณะเดียวกันก็เคารพการควบคุมความเป็นส่วนตัว
ระดับเบราว์เซอร์
ชุด SameSite: None
SameSite:
None
ทำเครื่องหมายคุกกี้อย่างชัดเจนสำหรับการเข้าถึงข้ามเว็บไซต์ เพื่อให้ส่งคุกกี้พร้อมกับ
คำขอที่มาจากบริบทของบุคคลที่สาม (เช่น iframe) ได้ แอตทริบิวต์นี้เป็น
ข้อกำหนดเบื้องต้นเพื่อให้คุกกี้ทำงานได้ในสถานการณ์แบบข้ามต้นทาง แม้ว่า
จะต้องรวมกับแอตทริบิวต์ Secure เพื่อให้เบราว์เซอร์ที่ทันสมัยยอมรับ
ชุด Partitioned
แอตทริบิวต์ Partitioned จะเลือกใช้คุกกี้ใน CHIPS (Cookies Having
Independent Partitioned
State) ซึ่งจะช่วยให้จัดเก็บคุกกี้แยกกันสำหรับแต่ละเว็บไซต์ระดับบนสุดได้ ซึ่งจะช่วยให้
คุกกี้ยังคงเข้าถึงได้ภายในบริบท iframe ของบุคคลที่สามที่เฉพาะเจาะจง
ทําให้สถานะเซสชันยังคงอยู่ได้โดยไม่ต้องเปิดใช้การติดตามข้ามเว็บไซต์ ผู้ใช้จะต้องลงชื่อเข้าใช้อีกครั้งสำหรับการฝังแต่ละรายการในเว็บไซต์อื่น
ปกป้องปลายทางด้วยนโยบายรักษาความปลอดภัยเนื้อหา
ในขณะที่นโยบายสิทธิ์จะกำหนดว่า iframe เรียกใช้ WebAuthn ได้หรือไม่ นโยบายรักษาความปลอดภัยเนื้อหา (CSP) จะกำหนดผู้ที่ได้รับอนุญาตให้โฮสต์ iframe
สำหรับปลายทางการตรวจสอบสิทธิ์ สิ่งสำคัญคือต้องตรวจสอบว่ามีเพียงเว็บไซต์พาร์ทเนอร์ที่ได้รับอนุญาตหรือพร็อพเพอร์ตี้ของคุณเองเท่านั้นที่โหลดเฟรมย่อยของการเข้าสู่ระบบได้ ซึ่งจะช่วยป้องกันการพยายามคลิกแจ็กกิ้งที่ไม่ได้รับอนุญาตก่อนที่ระบบจะโหลด UI ได้
ใช้ frame-ancestors
frame-ancestors
คำสั่ง
กำหนดหน้าหลักที่ถูกต้องซึ่งฝังเว็บไซต์ของคุณได้ การเพิ่มโดเมนลงใน
คำสั่งนี้จะช่วยให้คุณอนุญาตโดเมนที่ได้รับอนุญาตให้ฝัง
เฟรมย่อยของการเข้าสู่ระบบได้
Content-Security-Policy: frame-ancestors 'self' https://parent-site.example.com;
นโยบายรักษาความปลอดภัยเนื้อหา: ความเข้ากันได้ของ frame-ancestors:
ชุด X-Frame-Options
ส่วนหัว X-Frame-Options รุ่นเดิมรองรับความสามารถที่คล้ายกัน แต่รองรับเฉพาะตัวเลือกไบนารี (DENY หรือ SAMEORIGIN) เท่านั้น ตั้งค่าทั้ง CSP frame-ancestors
และ X-Frame-Options: DENY ในกรณีที่เบราว์เซอร์ไม่รองรับ CSP ระบบจะ
จัดลำดับความสำคัญของ CSP เสมอในที่ที่รองรับ
X-Frame-Options: DENY
ความเข้ากันได้ของ X-Frame-Options:
เชื่อถือ แต่ยืนยันฝั่งเซิร์ฟเวอร์
การตรวจสอบฝั่งไคลเอ็นต์ของเบราว์เซอร์จะประเมินเจตนาและสิทธิ์ แต่เซิร์ฟเวอร์ เป็นผู้พิจารณาความน่าเชื่อถือขั้นสุดท้าย ยืนยันการตอบกลับในเซิร์ฟเวอร์ Relying Party (RP) เพื่อให้แน่ใจว่าบริบทถูกต้องและมีการลงนาม
เพย์โหลดข้อมูลฝั่งไคลเอ็นต์
ข้อมูลไคลเอ็นต์ WebAuthn มีพารามิเตอร์ที่ออกแบบมาโดยเฉพาะเพื่อช่วยคุณ ยืนยันบริบทของคำขอที่ทำภายใน iframe ดังนี้
crossOrigin(บูลีน): ระบุว่ามีการเรียกใช้ WebAuthn API ภายใน iframe ข้ามต้นทางหรือไม่ หากสถาปัตยกรรมของคุณอาศัย iframe เซิร์ฟเวอร์ของคุณต้องบังคับใช้ว่าค่าของแฟล็กนี้คือtruetopOrigin(สตริง): ต้นทางของบริบทการเรียกดูระดับบนสุด (สิ่งที่ มองเห็นได้ในแถบที่อยู่ของเบราว์เซอร์) เซิร์ฟเวอร์ต้องยืนยันค่านี้กับ รายการต้นทางของผู้ปกครองที่ได้รับอนุญาตซึ่งเป็นที่รู้จัก
รายการตรวจสอบการยืนยัน
หากต้องการยืนยันการตอบกลับของเครื่องมือตรวจสอบสิทธิ์ในเซิร์ฟเวอร์ ให้ทำตามขั้นตอนต่อไปนี้
- แยกวิเคราะห์และถอดรหัส
collectedClientDataที่ลงชื่อแล้วจากคำตอบของเครื่องมือตรวจสอบสิทธิ์ - ตรวจสอบว่า
typeตรงกับพิธี (webauthn.getหรือwebauthn.create) - ยืนยันการแสดงตัวและลายเซ็นของผู้ใช้
- หากคำขอมีจุดประสงค์ที่จะมาจากโครงสร้าง iframe ให้ทำดังนี้
- บังคับใช้
crossOrigin === true - บังคับให้
topOriginตรงกับรายการต้นทางหลักที่ได้รับอนุญาต
- บังคับใช้
สร้างเซสชันอย่างปลอดภัยโดยใช้ postMessage()
หากต้องการสร้างเซสชันอย่างน่าเชื่อถือ iframe ต้องส่งโทเค็นการตรวจสอบสิทธิ์
กลับไปยังหน้าหลักโดยใช้ postMessage() เพื่อให้หน้าหลักจัดการสถานะเซสชัน
ในบริบทของบุคคลที่หนึ่งของตนเองได้
เวิร์กโฟลว์ที่ปลอดภัย
หากต้องการสร้างเซสชันที่ปลอดภัย ให้ทำตามเวิร์กโฟลว์นี้
- ตรวจสอบว่า URL ของ iframe
srcมีพารามิเตอร์การค้นหาnonceและoriginดังนี้- ใช้ค่าแบบสุ่มสำหรับ
noncenonceทำหน้าที่เป็นโทเค็นการยืนยันความปลอดภัย เพื่อให้แน่ใจว่าโทเค็นการตรวจสอบสิทธิ์ที่ได้รับจาก iframe ตรงกับเซสชันที่เฉพาะเจาะจงซึ่งเริ่มต้นโดย หน้าหลักอย่างถูกต้อง - ใช้โดเมนเฟรมหลักสำหรับ
originพารามิเตอร์originระบุต้นทางของหน้าหลัก ซึ่งช่วยให้ iframe ระบุบริบทที่ได้รับอนุญาตซึ่งมีการฝัง iframe ได้อย่างปลอดภัย
- ใช้ค่าแบบสุ่มสำหรับ
- Iframe จะทำการตรวจสอบสิทธิ์ WebAuthn กับเซิร์ฟเวอร์ของตัวเองให้เสร็จสมบูรณ์
เซิร์ฟเวอร์ iframe จะออกโทเค็น เช่น JWT ที่มี
nonceและส่งต่อให้หน้าหลัก// Extract nonce and origin from the URL params const urlParams = new URLSearchParams(window.location.search); const nonce = urlParams.get('nonce'); const origin = urlParams.get('origin'); if (!nonce || !origin) { alert('Nonce or origin is missing in the URL'); return; } // Create a JWT const response = await post('/createToken', { nonce, origin }); const token = response.token; // Post the JWT to the parent frame window.parent.postMessage({ token }, origin);หน้าหลักจะรอรับเหตุการณ์
messageตรวจสอบต้นทางของผู้ส่ง และยืนยันโทเค็นwindow.addEventListener("message", (event) => { if (event.origin !== "https://embedded-auth.example.com") return; // Verify the received JWT const result = await post('/verifyIdToken', { token: event.data.token, origin: provider.origin, }); });หน้าหลักจะคงเซสชันไว้หากยืนยัน JWT สำเร็จ
ทั้งผู้ส่งและผู้รับมีหน้าที่รับผิดชอบด้านความปลอดภัยร่วมกัน ดังนี้
- ผู้ส่ง (iframe): ระบุต้นทางเป้าหมายที่เข้มงวดเสมอเมื่อส่งข้อความ (ห้ามใช้
"*") - ผู้รับ (ผู้ปกครอง): ตรวจสอบ
event.originเสมอเมื่อได้รับ ข้อความเพื่อป้องกันการปลอมแปลงต้นทาง
บทสรุป
การใช้ iframe อย่างปลอดภัยขึ้นอยู่กับนโยบายสิทธิ์สำหรับการเปิดใช้, CSP สำหรับ
การจำกัด, คุกกี้ของบุคคลที่สามที่แบ่งพาร์ติชันสำหรับการคงอยู่ของเซสชัน,
การยืนยันฝั่งเซิร์ฟเวอร์ของบริบทไคลเอ็นต์ และการส่งต่อเซสชันที่รับรู้บริบท
โดยใช้ postMessage()
ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อที่เกี่ยวข้องได้โดยติดตามบล็อกนักพัฒนาแอป Chrome ของ Google และสำรวจแหล่งข้อมูลเพิ่มเติมที่เอกสารประกอบเกี่ยวกับตัวตนของนักพัฒนาแอป Chrome