พาสคีย์ภายใน iframe

องค์กรมักจะฝังหน้าลงชื่อเข้าใช้ภายใน 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())

ข้อกำหนดสำหรับการเปิดใช้

การเปิดใช้ความสามารถเหล่านี้ต้องมีการจัดแนวทั้งในคำตอบของเซิร์ฟเวอร์หลัก และมาร์กอัปฝั่งไคลเอ็นต์

Permissions-Policy: publickey-credentials-get=(self "https://embedded-auth.example.com")

นโยบายสิทธิ์: ความเข้ากันได้ของ publickey-credentials-get:

Browser Support

  • Chrome: 88.
  • Edge: 88.
  • Firefox: not supported.
  • Safari: not supported.

Source

นโยบายสิทธิ์: ความเข้ากันได้ของ publickey-credentials-create:

Browser Support

  • Chrome: 88.
  • Edge: 88.
  • Firefox: not supported.
  • Safari: not supported.

Source

  • แอตทริบิวต์ 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

  • Chrome: 84.
  • Edge: 84.
  • Firefox: 118.
  • Safari: not supported.

ความเข้ากันได้ของ iframe allow="publickey-credentials-create":

Browser Support

  • Chrome: not supported.
  • Edge: not supported.
  • Firefox: 123.
  • Safari: not supported.

เปิดใช้คุกกี้ของบุคคลที่สามที่แบ่งพาร์ติชัน

ต้องสร้างและรักษาเซสชันไว้ภายใน 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:

Browser Support

  • Chrome: 40.
  • Edge: 15.
  • Firefox: 58.
  • Safari: 10.

Source

ชุด X-Frame-Options

ส่วนหัว X-Frame-Options รุ่นเดิมรองรับความสามารถที่คล้ายกัน แต่รองรับเฉพาะตัวเลือกไบนารี (DENY หรือ SAMEORIGIN) เท่านั้น ตั้งค่าทั้ง CSP frame-ancestors และ X-Frame-Options: DENY ในกรณีที่เบราว์เซอร์ไม่รองรับ CSP ระบบจะ จัดลำดับความสำคัญของ CSP เสมอในที่ที่รองรับ

X-Frame-Options: DENY

ความเข้ากันได้ของ X-Frame-Options:

Browser Support

  • Chrome: 4.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 4.

Source

เชื่อถือ แต่ยืนยันฝั่งเซิร์ฟเวอร์

การตรวจสอบฝั่งไคลเอ็นต์ของเบราว์เซอร์จะประเมินเจตนาและสิทธิ์ แต่เซิร์ฟเวอร์ เป็นผู้พิจารณาความน่าเชื่อถือขั้นสุดท้าย ยืนยันการตอบกลับในเซิร์ฟเวอร์ Relying Party (RP) เพื่อให้แน่ใจว่าบริบทถูกต้องและมีการลงนาม

เพย์โหลดข้อมูลฝั่งไคลเอ็นต์

ข้อมูลไคลเอ็นต์ WebAuthn มีพารามิเตอร์ที่ออกแบบมาโดยเฉพาะเพื่อช่วยคุณ ยืนยันบริบทของคำขอที่ทำภายใน iframe ดังนี้

  • crossOrigin (บูลีน): ระบุว่ามีการเรียกใช้ WebAuthn API ภายใน iframe ข้ามต้นทางหรือไม่ หากสถาปัตยกรรมของคุณอาศัย iframe เซิร์ฟเวอร์ของคุณต้องบังคับใช้ว่าค่าของแฟล็กนี้คือ true
  • topOrigin (สตริง): ต้นทางของบริบทการเรียกดูระดับบนสุด (สิ่งที่ มองเห็นได้ในแถบที่อยู่ของเบราว์เซอร์) เซิร์ฟเวอร์ต้องยืนยันค่านี้กับ รายการต้นทางของผู้ปกครองที่ได้รับอนุญาตซึ่งเป็นที่รู้จัก

รายการตรวจสอบการยืนยัน

หากต้องการยืนยันการตอบกลับของเครื่องมือตรวจสอบสิทธิ์ในเซิร์ฟเวอร์ ให้ทำตามขั้นตอนต่อไปนี้

  1. แยกวิเคราะห์และถอดรหัส collectedClientData ที่ลงชื่อแล้วจากคำตอบของเครื่องมือตรวจสอบสิทธิ์
  2. ตรวจสอบว่าtypeตรงกับพิธี (webauthn.get หรือ webauthn.create)
  3. ยืนยันการแสดงตัวและลายเซ็นของผู้ใช้
  4. หากคำขอมีจุดประสงค์ที่จะมาจากโครงสร้าง iframe ให้ทำดังนี้
    • บังคับใช้ crossOrigin === true
    • บังคับให้ topOrigin ตรงกับรายการต้นทางหลักที่ได้รับอนุญาต

สร้างเซสชันอย่างปลอดภัยโดยใช้ postMessage()

หากต้องการสร้างเซสชันอย่างน่าเชื่อถือ iframe ต้องส่งโทเค็นการตรวจสอบสิทธิ์ กลับไปยังหน้าหลักโดยใช้ postMessage() เพื่อให้หน้าหลักจัดการสถานะเซสชัน ในบริบทของบุคคลที่หนึ่งของตนเองได้

เวิร์กโฟลว์ที่ปลอดภัย

หากต้องการสร้างเซสชันที่ปลอดภัย ให้ทำตามเวิร์กโฟลว์นี้

  1. ตรวจสอบว่า URL ของ iframe src มีพารามิเตอร์การค้นหา nonce และ origin ดังนี้
    • ใช้ค่าแบบสุ่มสำหรับ nonce nonceทำหน้าที่เป็นโทเค็นการยืนยันความปลอดภัย เพื่อให้แน่ใจว่าโทเค็นการตรวจสอบสิทธิ์ที่ได้รับจาก iframe ตรงกับเซสชันที่เฉพาะเจาะจงซึ่งเริ่มต้นโดย หน้าหลักอย่างถูกต้อง
    • ใช้โดเมนเฟรมหลักสำหรับ origin พารามิเตอร์ origin ระบุต้นทางของหน้าหลัก ซึ่งช่วยให้ iframe ระบุบริบทที่ได้รับอนุญาตซึ่งมีการฝัง iframe ได้อย่างปลอดภัย
  2. Iframe จะทำการตรวจสอบสิทธิ์ WebAuthn กับเซิร์ฟเวอร์ของตัวเองให้เสร็จสมบูรณ์
  3. เซิร์ฟเวอร์ 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);
    
  4. หน้าหลักจะรอรับเหตุการณ์ 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,
      });
    });
    
  5. หน้าหลักจะคงเซสชันไว้หากยืนยัน JWT สำเร็จ

ทั้งผู้ส่งและผู้รับมีหน้าที่รับผิดชอบด้านความปลอดภัยร่วมกัน ดังนี้

  • ผู้ส่ง (iframe): ระบุต้นทางเป้าหมายที่เข้มงวดเสมอเมื่อส่งข้อความ (ห้ามใช้ "*")
  • ผู้รับ (ผู้ปกครอง): ตรวจสอบ event.origin เสมอเมื่อได้รับ ข้อความเพื่อป้องกันการปลอมแปลงต้นทาง

บทสรุป

การใช้ iframe อย่างปลอดภัยขึ้นอยู่กับนโยบายสิทธิ์สำหรับการเปิดใช้, CSP สำหรับ การจำกัด, คุกกี้ของบุคคลที่สามที่แบ่งพาร์ติชันสำหรับการคงอยู่ของเซสชัน, การยืนยันฝั่งเซิร์ฟเวอร์ของบริบทไคลเอ็นต์ และการส่งต่อเซสชันที่รับรู้บริบท โดยใช้ postMessage()

ดูข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อที่เกี่ยวข้องได้โดยติดตามบล็อกนักพัฒนาแอป Chrome ของ Google และสำรวจแหล่งข้อมูลเพิ่มเติมที่เอกสารประกอบเกี่ยวกับตัวตนของนักพัฒนาแอป Chrome