เล่นอย่างปลอดภัยใน IFrame ที่แซนด์บ็อกซ์

การสร้างประสบการณ์ที่สมบูรณ์บนเว็บในปัจจุบันนั้นแทบจะหลีกเลี่ยงไม่ได้ คือการฝังคอมโพเนนต์และเนื้อหาที่คุณควบคุมไม่ได้อย่างแท้จริง วิดเจ็ตของบุคคลที่สามสามารถกระตุ้นการมีส่วนร่วมและมีส่วนสําคัญอย่างยิ่งในประสบการณ์โดยรวมของผู้ใช้ และบางครั้งเนื้อหาที่ผู้ใช้สร้างขึ้นก็สำคัญกว่าเนื้อหาในเว็บไซต์ การเลิกใช้นโยบายทั้งสองนี้ไม่ใช่ตัวเลือกที่ดี แต่ทั้ง 2 อย่างจะเพิ่มความเสี่ยงที่เกิดข้อผิดพลาดบางอย่าง BadTM ในเว็บไซต์ของคุณ วิดเจ็ตแต่ละรายการที่คุณฝัง ไม่ว่าจะเป็นโฆษณา วิดเจ็ตโซเชียลมีเดียทุกรายการ อาจเป็นเวกเตอร์การโจมตีสำหรับผู้ที่มีเจตนาร้าย

นโยบายรักษาความปลอดภัยเนื้อหา (CSP) สามารถลดความเสี่ยงที่เกี่ยวข้องกับเนื้อหาทั้ง 2 ประเภทนี้ได้ด้วยการให้คุณเพิ่มแหล่งที่มาของสคริปต์และเนื้อหาอื่นๆ ที่เชื่อถือได้โดยเฉพาะลงในรายการที่อนุญาต นี่เป็นก้าวสำคัญในทิศทางที่ถูกต้อง แต่ควรทราบว่าการป้องกันที่คำสั่ง CSP ส่วนใหญ่มอบให้เป็นแบบ 2 ค่า คืออนุญาตหรือไม่อนุญาต บางครั้งการพูดว่า "ฉันไม่แน่ใจว่าเชื่อถือแหล่งที่มาของเนื้อหานี้จริงๆ แต่มันสวยมาก ฝังมันเลย โปรด เบราว์เซอร์ แต่อย่าทำให้เว็บไซต์ของฉันพัง"

สิทธิ์ขั้นต่ำที่สุด

สรุปแล้ว เรากําลังมองหากลไกที่จะทําให้เราให้สิทธิ์เนื้อหาที่ฝังไว้ในระดับความสามารถขั้นต่ำที่จําเป็นต่อการใช้งานเท่านั้น หากวิดเจ็ต ไม่ต้องแสดงหน้าต่างใหม่ การนำสิทธิ์เข้าถึง window.open ออกก็ไม่น่ากังวล หากแอปไม่จำเป็นต้องใช้ Flash การปิดใช้การรองรับปลั๊กอินจะไม่ทำให้เกิดปัญหา เราจะมีความปลอดภัยสูงสุดเท่าที่เราจะทำได้หากปฏิบัติตามหลักการให้สิทธิ์ขั้นต่ำ และบล็อกแต่ละฟีเจอร์ที่ไม่ได้เกี่ยวข้องกับฟังก์ชันการทำงานที่ต้องการใช้โดยตรง ผลลัพธ์ที่ได้คือเราไม่จำเป็นต้องเชื่ออย่างสุ่มสี่สุ่มห้าอีกต่อไปว่าเนื้อหาที่ฝังบางส่วนจะไม่ใช้ประโยชน์จากสิทธิ์ที่ไม่ควรใช้ เพียงแค่ไม่มีสิทธิ์เข้าถึงฟังก์ชันการทำงานตั้งแต่แรก

องค์ประกอบ iframe เป็นก้าวแรกสู่เฟรมเวิร์กที่ดีสําหรับโซลูชันดังกล่าว การโหลดคอมโพเนนต์ที่ไม่น่าเชื่อถือบางอย่างใน iframe จะช่วยแยกแอปพลิเคชันของคุณออกจากเนื้อหาที่ต้องการโหลด เนื้อหาในกรอบจะไม่มีสิทธิ์เข้าถึง DOM ของหน้าเว็บหรือข้อมูลที่จัดเก็บไว้ในเครื่อง แต่จะวาดในตำแหน่งใดก็ได้ในหน้าเว็บ โดยขอบเขตจะจำกัดอยู่ที่ขอบของกรอบ อย่างไรก็ตาม การแยกดังกล่าวไม่ได้มีประสิทธิภาพมากนัก หน้าเว็บที่รวมอยู่ยังมีตัวเลือกต่างๆ อีกหลายอย่างสำหรับลักษณะการทำงานที่รบกวนหรือเป็นอันตราย เช่น วิดีโอ ปลั๊กอิน และป๊อปอัปที่เล่นอัตโนมัติ

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

เชื่อ แต่ต้องตรวจสอบ

ปุ่ม "ทวีต" ของ Twitter เป็นตัวอย่างที่ยอดเยี่ยมของฟังก์ชันการทำงานที่ฝังลงในเว็บไซต์ได้อย่างปลอดภัยมากขึ้นผ่านแซนด์บ็อกซ์ Twitter ให้คุณฝังปุ่มผ่าน iframe ได้โดยใช้โค้ดต่อไปนี้

<iframe src="https://platform.twitter.com/widgets/tweet_button.html"
        style="border: 0; width:130px; height:20px;"></iframe>

มาดูกันว่าเราจะล็อกอะไรได้บ้าง โดยเราจะตรวจสอบความสามารถที่จําเป็นสำหรับปุ่มนี้อย่างละเอียด HTML ที่โหลดลงในเฟรมจะเรียกใช้ JavaScript เล็กน้อยจากเซิร์ฟเวอร์ของ Twitter และสร้างป๊อปอัปที่มีอินเทอร์เฟซการทวีตเมื่อมีการคลิก อินเทอร์เฟซนั้นจำเป็นต้องเข้าถึงคุกกี้ของ Twitter เพื่อเชื่อมโยงทวีตกับบัญชีที่ถูกต้อง รวมถึงต้องมีความสามารถในการส่งแบบฟอร์มการทวีต เสร็จแล้ว เฟรมไม่จำเป็นต้องโหลดปลั๊กอินใดๆ ไม่จำเป็นต้องเลื่อนไปยังหน้าต่างระดับบนสุด หรือฟังก์ชันอื่นๆ อีกมากมาย เนื่องจากไม่จำเป็นต้องมีสิทธิ์เหล่านั้น เราจึงจะนําสิทธิ์เหล่านั้นออกโดยการใช้แซนด์บ็อกซ์กับเนื้อหาของเฟรม

แซนด์บ็อกซ์ทำงานตามรายการที่อนุญาตพิเศษ เราเริ่มต้นด้วยการนำสิทธิ์ทั้งหมดที่เป็นไปได้ออก จากนั้นเปิดความสามารถแต่ละรายการอีกครั้งด้วยการเพิ่ม Flag ที่เจาะจงลงในการกำหนดค่าของแซนด์บ็อกซ์ สำหรับวิดเจ็ต Twitter เราตัดสินใจที่จะเปิดใช้ JavaScript, ป๊อปอัป, การส่งแบบฟอร์ม และคุกกี้ของ twitter.com ซึ่งทําได้โดยการเพิ่มแอตทริบิวต์ sandbox ลงใน iframe พร้อมค่าต่อไปนี้

<iframe sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
    src="https://platform.twitter.com/widgets/tweet_button.html"
    style="border: 0; width:130px; height:20px;"></iframe>

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

การควบคุมความสามารถแบบละเอียด

เราได้เห็น Flag ที่ใช้กับแซนด์บ็อกซ์ที่เป็นไปได้ 2-3 รายการในตัวอย่างด้านบน ตอนนี้มาเจาะลึกการทำงานภายในของแอตทริบิวต์กัน

เมื่อใช้ iframe ที่มีแอตทริบิวต์แซนด์บ็อกซ์ว่าง เอกสารที่ใส่กรอบจะอยู่ในแซนด์บ็อกซ์โดยสมบูรณ์ ซึ่งจะขึ้นอยู่กับข้อจำกัดต่อไปนี้

  • JavaScript จะไม่ทำงานในเอกสารที่มีกรอบ ซึ่งไม่ได้รวมถึง JavaScript ที่โหลดอย่างชัดเจนผ่านแท็กสคริปต์ แต่รวมถึงเครื่องจัดการเหตุการณ์แบบอินไลน์และ URL ของ javascript: ยังหมายความว่าเนื้อหาที่อยู่ในแท็ก noscript จะปรากฏเหมือนกับที่ผู้ใช้ปิดใช้สคริปต์ด้วยตนเอง
  • ระบบจะโหลดเอกสารที่มีกรอบลงในต้นทางที่ไม่ซ้ำกัน ซึ่งหมายความว่าการตรวจสอบต้นทางเดียวกันทั้งหมดจะดำเนินการไม่สำเร็จ ต้นทางที่ไม่ซ้ำกันจะไม่ตรงกับต้นทางอื่นๆ เลย แม้แต่ต้นทางเดียวกันเอง ผลกระทบอื่นๆ ของการดำเนินการนี้ ได้แก่ เอกสารจะไม่มีสิทธิ์เข้าถึงข้อมูลที่จัดเก็บไว้ในคุกกี้ของต้นทางหรือกลไกการจัดเก็บอื่นๆ (ที่จัดเก็บ DOM, Indexed DB ฯลฯ)
  • เอกสารที่มีกรอบจะสร้างหน้าต่างหรือกล่องโต้ตอบใหม่ไม่ได้ (เช่น ผ่าน window.open หรือ target="_blank")
  • ส่งแบบฟอร์มไม่ได้
  • ปลั๊กอินจะไม่โหลด
  • เอกสารที่อยู่ในกรอบจะไปยังส่วนต่างๆ ได้ด้วยตนเองเท่านั้น แต่จะไปยังเอกสารระดับบนสุดไม่ได้ การตั้งค่า window.top.location จะแสดงข้อยกเว้น และการคลิกลิงก์ที่มี target="_top" จะไม่มีผล
  • ระบบจะบล็อกฟีเจอร์ที่ทริกเกอร์โดยอัตโนมัติ (องค์ประกอบแบบโฟกัสอัตโนมัติของแบบฟอร์ม วิดีโอที่เล่นอัตโนมัติ ฯลฯ)
  • ไม่สามารถรับการล็อกเคอร์เซอร์
  • ระบบจะไม่สนใจแอตทริบิวต์ seamless ใน iframes ที่เอกสารที่มีกรอบมี

การดำเนินการนี้เข้มงวดมาก และเอกสารที่โหลดลงใน iframe ที่ทำงานในระบบแซนด์บ็อกซ์อย่างเต็มรูปแบบมีความเสี่ยงน้อยมาก แต่แน่นอนว่าวิธีนี้ก็ไม่ได้มีประโยชน์มากนัก คุณอาจใช้แซนด์บ็อกซ์แบบเต็มกับเนื้อหาแบบคงที่บางรายการได้ แต่ส่วนใหญ่แล้วคุณจะต้องผ่อนคลายกฎระเบียบลงบ้าง

ยกเว้นปลั๊กอิน คุณสามารถยกเลิกข้อจำกัดเหล่านี้ได้โดยการใส่ Flag ลงในค่าของแอตทริบิวต์แซนด์บ็อกซ์ เอกสารที่อยู่ในแซนด์บ็อกซ์จะไม่สามารถเรียกใช้ปลั๊กอินได้ เนื่องจากปลั๊กอินเป็นโค้ดเนทีฟที่ไม่ได้อยู่ในแซนด์บ็อกซ์ แต่ส่วนอื่นๆ นั้นใช้ได้

  • allow-forms อนุญาตให้ส่งแบบฟอร์ม
  • allow-popups อนุญาตป๊อปอัป (น่าตกใจ)
  • allow-pointer-lock อนุญาตให้ล็อกเคอร์เซอร์ (แปลกใจไหม)
  • allow-same-origin ช่วยให้เอกสารคงต้นทางไว้ หน้าเว็บที่โหลดจาก https://example.com/ จะยังคงเข้าถึงข้อมูลของต้นทางนั้นได้
  • allow-scripts อนุญาตให้เรียกใช้ JavaScript และอนุญาตให้ฟีเจอร์ทริกเกอร์โดยอัตโนมัติ (เนื่องจากติดตั้งใช้งานผ่าน JavaScript ได้ง่ายๆ)
  • allow-top-navigation ช่วยให้เอกสารออกจากเฟรมได้โดยการนำทางหน้าต่างระดับบนสุด

เมื่อพิจารณาข้อมูลเหล่านี้แล้ว เราประเมินสาเหตุที่ทำให้เกิด Flag Sandboxing ชุดหนึ่งๆ ในตัวอย่าง Twitter ด้านบนได้ดังนี้

  • ต้องมี allow-scripts เนื่องจากหน้าเว็บที่โหลดลงในเฟรมจะเรียกใช้ JavaScript บางอย่างเพื่อจัดการกับการโต้ตอบของผู้ใช้
  • ต้องมี allow-popups เนื่องจากปุ่มนี้จะแสดงแบบฟอร์มการทวีตในหน้าต่างใหม่
  • ต้องระบุ allow-forms เนื่องจากควรส่งแบบฟอร์มการทวีตได้
  • จำเป็นต้องมี allow-same-origin เนื่องจากคุกกี้ของ twitter.com จะไม่สามารถเข้าถึงได้ และผู้ใช้ไม่สามารถเข้าสู่ระบบเพื่อโพสต์แบบฟอร์มได้

สิ่งที่ควรทราบอย่างหนึ่งคือ Flag ที่ใช้กับแซนด์บ็อกซ์ในเฟรมจะมีผลกับหน้าต่างหรือเฟรมที่สร้างในแซนด์บ็อกซ์ด้วย ซึ่งหมายความว่าเราต้องเพิ่ม allow-forms ลงในแซนด์บ็อกซ์ของเฟรม แม้ว่าแบบฟอร์มจะอยู่ในหน้าต่างที่เฟรมปรากฏขึ้นเท่านั้น

เมื่อใช้แอตทริบิวต์ sandbox วิดเจ็ตจะได้รับเฉพาะสิทธิ์ที่จําเป็น และความสามารถต่างๆ เช่น ปลั๊กอิน การนำทางด้านบน และการล็อกเคอร์เซอร์จะยังคงถูกบล็อก เราได้ลดความเสี่ยงในการฝังวิดเจ็ตโดยไม่ก่อให้เกิดผลกระทบใดๆ นี่คือชัยชนะสำหรับทุกคน

การแยกสิทธิ์

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

ต่อไปเราจะถามคำถามนี้ว่า ถ้าโค้ดของคุณไม่ต้องใช้ปลั๊กอิน ทำไมต้องให้สิทธิ์เข้าถึงปลั๊กอิน ในกรณีที่ดีที่สุด สิทธิ์นี้จะเป็นสิทธิ์ที่คุณไม่เคยใช้เลย แต่ในกรณีที่เลวร้ายที่สุด สิทธิ์นี้อาจเป็นเวกเตอร์ที่อาจทำให้ผู้โจมตีเข้ามาได้ โค้ดของทุกคนมีข้อบกพร่อง และในความเป็นจริงแล้วทุกแอปพลิเคชันก็เสี่ยงต่อการถูกแสวงหาประโยชน์ไม่ว่าในทางใดทางหนึ่ง การใช้แซนด์บ็อกซ์กับโค้ดของคุณเองหมายความว่าแม้ว่าผู้โจมตีจะแทรกแซงแอปพลิเคชันของคุณได้สำเร็จ แต่ผู้โจมตีก็จะไม่ได้รับสิทธิ์เข้าถึงเต็มรูปแบบในต้นทางของแอปพลิเคชัน แต่จะทําได้เพียงสิ่งที่แอปพลิเคชันทำได้เท่านั้น แม้จะยังคงเป็นปัญหาอยู่ แต่ก็ไม่ใช่ปัญหาร้ายแรงที่สุด

คุณลดความเสี่ยงได้มากขึ้นด้วยการแบ่งแอปพลิเคชันออกเป็นส่วนๆ ตามความเหมาะสม และทำให้แต่ละส่วนอยู่ในแซนด์บ็อกซ์ที่มีสิทธิ์น้อยที่สุดเท่าที่จะเป็นไปได้ เทคนิคนี้พบได้ทั่วไปในโค้ดเนทีฟ เช่น Chrome จะแบ่งตัวเองออกเป็นกระบวนการเบราว์เซอร์ที่มีสิทธิ์สูงซึ่งเข้าถึงฮาร์ดไดรฟ์ในเครื่องได้และสร้างการเชื่อมต่อเครือข่ายได้ รวมถึงกระบวนการแสดงผลที่มีสิทธิ์ต่ำจำนวนมากซึ่งทํางานหนักในการแยกวิเคราะห์เนื้อหาที่ไม่น่าเชื่อถือ โปรแกรมแสดงผลไม่จำเป็นต้องเข้าถึงดิสก์ เนื่องจากเบราว์เซอร์จะเป็นผู้ให้ข้อมูลทั้งหมดที่จำเป็นในการแสดงผลหน้าเว็บ แม้ว่าแฮ็กเกอร์ที่ฉลาดจะพบวิธีทำให้โปรแกรมแสดงผลเสียหาย แต่ก็ยังทำอันตรายได้ไม่มากนัก เนื่องจากโปรแกรมแสดงผลไม่สามารถดำเนินการใดๆ ที่น่าสนใจได้ด้วยตัวเอง สิทธิ์เข้าถึงที่มีสิทธิ์ระดับสูงทั้งหมดต้องส่งผ่านกระบวนการของเบราว์เซอร์ ผู้โจมตีจะต้องหาช่องโหว่หลายจุดในระบบส่วนต่างๆ เพื่อที่จะสร้างความเสียหาย ซึ่งจะช่วยลดความเสี่ยงในการลักลอบใช้บัญชีได้อย่างมาก

กำลังแซนด์บ็อกซ์ eval() อย่างปลอดภัย

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

Evalbox เป็นแอปพลิเคชันที่น่าสนใจซึ่งจะรับสตริงและประเมินเป็น JavaScript ว้าว ถูกต้องไหม เหมือนที่คุณรอคอย มาตลอดหลายปีที่ผ่านมา แน่นอนว่าแอปพลิเคชันนี้ค่อนข้างอันตราย เนื่องจากการอนุญาตให้ JavaScript ทำงานโดยพลการหมายความว่าข้อมูลทั้งหมดที่ต้นทางมีให้สามารถเข้าถึงได้ เราจะลดความเสี่ยงที่จะเกิด "เหตุการณ์ร้ายแรง" ขึ้นโดยการตรวจสอบว่าโค้ดทำงานภายในแซนด์บ็อกซ์ ซึ่งทำให้ปลอดภัยขึ้นมาก เราจะเขียนโค้ดจากด้านในออก โดยเริ่มจากเนื้อหาในเฟรม

<!-- frame.html -->
<!DOCTYPE html>
<html>
    <head>
    <title>Evalbox's Frame</title>
    <script>
        window.addEventListener('message', function (e) {
        var mainWindow = e.source;
        var result = '';
        try {
            result = eval(e.data);
        } catch (e) {
            result = 'eval() threw an exception.';
        }
        mainWindow.postMessage(result, event.origin);
        });
    </script>
    </head>
</html>

ภายในเฟรม เรามีเอกสารขนาดเล็กที่คอยตรวจจับข้อความจากระดับบนสุด โดยการเชื่อมโยงเข้ากับเหตุการณ์ message ของออบเจ็กต์ window เมื่อใดก็ตามที่รายการหลักเรียกใช้ postMessage ในเนื้อหาของ iframe เหตุการณ์นี้จะทริกเกอร์ขึ้น ซึ่งทำให้เราเข้าถึงสตริงที่รายการหลักต้องการให้เราเรียกใช้ได้

ในตัวแฮนเดิล เราจะดึงแอตทริบิวต์ source ของเหตุการณ์ ซึ่งเป็นหน้าต่างหลัก เราจะนำผลลัพธ์ที่ได้มาจากการทุ่มเททำงานของเรากลับมาเมื่อเสร็จสิ้น จากนั้นเราจะดำเนินการในส่วนที่ยาก โดยส่งข้อมูลที่ได้จากคุณไปยัง eval() การเรียกนี้ได้รับการรวมไว้ในบล็อก try เนื่องจากการดำเนินการที่ถูกห้ามภายใน iframe ที่อยู่ในแซนด์บ็อกซ์จะสร้างข้อยกเว้น DOM บ่อยครั้ง เราจะจับข้อยกเว้นเหล่านั้นและรายงานข้อความแสดงข้อผิดพลาดที่เข้าใจง่ายแทน สุดท้าย เราจะโพสต์ผลลัพธ์กลับไปยังหน้าต่างหลัก ขั้นตอนนี้ค่อนข้างตรงไปตรงมา

การดำเนินการกับรายการหลักก็ซับซ้อนไม่แพ้กัน เราจะสร้าง UI ขนาดเล็กที่มี textarea สําหรับโค้ด และ button สําหรับการดําเนินการ และเราจะดึง frame.html ผ่าน iframe ที่ใช้แซนด์บ็อกซ์ ซึ่งอนุญาตเฉพาะการเรียกใช้สคริปต์เท่านั้น

<textarea id='code'></textarea>
<button id='safe'>eval() in a sandboxed frame.</button>
<iframe sandbox='allow-scripts'
        id='sandboxed'
        src='frame.html'></iframe>

ตอนนี้เราจะเดินสายไฟเพื่อใช้งาน ก่อนอื่น เราจะฟังความคิดเห็นจาก iframe และ alert() ให้กับผู้ใช้ ถ้าเป็นแอปพลิเคชันจริง น่าจะน่ารำคาญน้อยกว่า

window.addEventListener('message',
    function (e) {
        // Sandboxed iframes which lack the 'allow-same-origin'
        // header have "null" rather than a valid origin. This means you still
        // have to be careful about accepting data via the messaging API you
        // create. Check that source, and validate those inputs!
        var frame = document.getElementById('sandboxed');
        if (e.origin === "null" &amp;&amp; e.source === frame.contentWindow)
        alert('Result: ' + e.data);
    });

ต่อไป เราจะเชื่อมต่อตัวแฮนเดิลเหตุการณ์กับการคลิก button เมื่อผู้ใช้คลิก เราจะดึงเนื้อหาปัจจุบันของ textarea และส่งไปยังเฟรมเพื่อดำเนินการ

function evaluate() {
    var frame = document.getElementById('sandboxed');
    var code = document.getElementById('code').value;
    // Note that we're sending the message to "*", rather than some specific
    // origin. Sandboxed iframes which lack the 'allow-same-origin' header
    // don't have an origin which you can target: you'll have to send to any
    // origin, which might alow some esoteric attacks. Validate your output!
    frame.contentWindow.postMessage(code, '*');
}

document.getElementById('safe').addEventListener('click', evaluate);

ง่ายใช่ไหม เราได้สร้าง API การประเมินที่เรียบง่ายมากและมั่นใจได้ว่าโค้ดที่ประเมินจะไม่มีสิทธิ์เข้าถึงข้อมูลที่ละเอียดอ่อน เช่น คุกกี้หรือพื้นที่เก็บข้อมูล DOM ในทำนองเดียวกัน โค้ดที่ประเมินจะไม่สามารถโหลดปลั๊กอิน หน้าต่างป๊อปอัป หรือกิจกรรมอื่นๆ ที่น่ารำคาญหรือเป็นอันตราย

คุณสามารถทำแบบเดียวกันนี้กับโค้ดของคุณเองได้โดยแบ่งแอปพลิเคชันโมโนลิธเป็นคอมโพเนนต์ที่มีวัตถุประสงค์เดียว แต่ละรายการสามารถรวมไว้ใน API การรับส่งข้อความแบบง่ายได้ เช่นเดียวกับที่เราเขียนไว้ข้างต้น หน้าต่างหลักที่มีสิทธิ์สูงสามารถทำหน้าที่เป็นตัวควบคุมและผู้ส่ง โดยส่งข้อความไปยังโมดูลที่เฉพาะเจาะจงซึ่งแต่ละโมดูลมีสิทธิ์น้อยที่สุดในการทำงาน คอยฟังผลลัพธ์ และตรวจสอบว่าแต่ละโมดูลได้รับข้อมูลที่จำเป็นเท่านั้น

อย่างไรก็ตาม โปรดทราบว่าคุณต้องระมัดระวังอย่างยิ่งเมื่อจัดการกับเนื้อหาที่มีกรอบซึ่งมาจากแหล่งที่มาเดียวกันกับเนื้อหาหลัก หากหน้าในhttps://example.com/แสดงเฟรมหน้าอื่นในต้นทางเดียวกันที่มีแซนด์บ็อกซ์ซึ่งมีทั้ง Flag allow-same-origin และ allow-scripts หน้าที่มีเฟรมจะเข้าถึงหน้าหลักและนำแอตทริบิวต์แซนด์บ็อกซ์ออกได้ทั้งหมด

เล่นในแซนด์บ็อกซ์

ขณะนี้แซนด์บ็อกซ์พร้อมให้ใช้งานในเบราว์เซอร์ต่างๆ เช่น Firefox 17 ขึ้นไป, IE10 ขึ้นไป และ Chrome ณ เวลาที่เขียนบทความนี้ (แน่นอนว่า caniuse มีตารางการรองรับที่อัปเดตอยู่เสมอ) การใช้แอตทริบิวต์ sandbox กับ iframes ที่รวมไว้ช่วยให้คุณให้สิทธิ์บางอย่างแก่เนื้อหาที่แสดงได้ แต่ให้สิทธิ์เฉพาะที่จำเป็นต่อการทำงานอย่างถูกต้องของเนื้อหา ซึ่งจะช่วยให้คุณลดความเสี่ยงที่เกี่ยวข้องกับการรวมเนื้อหาของบุคคลที่สามได้มากขึ้นนอกเหนือจากที่ทำได้ด้วยนโยบายรักษาความปลอดภัยเนื้อหา

นอกจากนี้ Sandboxing ยังถือเป็นเทคนิคที่มีประสิทธิภาพในการลดความเสี่ยงที่ผู้โจมตีที่ฉลาดจะใช้ประโยชน์จากช่องโหว่ในโค้ดของคุณได้ การแยกแอปพลิเคชันแบบโมโนลิธิกออกเป็นชุดบริการที่ใช้แซนด์บ็อกซ์ ซึ่งแต่ละบริการมีหน้าที่รับผิดชอบฟังก์ชันการทำงานแบบสแตนด์อโลนเพียงส่วนเล็กๆ จะทำให้ผู้โจมตีต้องเจาะทั้งเนื้อหาของเฟรมที่เฉพาะเจาะจงและตัวควบคุมเฟรมด้วย นี่เป็นงานที่ยากขึ้นมาก โดยเฉพาะอย่างยิ่งเมื่อตัวควบคุมอาจลดขอบเขตลงได้อย่างมาก คุณสามารถใช้ความพยายามที่เกี่ยวข้องกับความปลอดภัยในการตรวจสอบโค้ดนั้นได้หากขอความช่วยเหลือจากเบราว์เซอร์เกี่ยวกับส่วนที่เหลือ

ไม่ใช่ว่าแซนด์บ็อกซ์จะเป็นทางออกแก้ปัญหาด้านความปลอดภัยบนอินเทอร์เน็ตโดยสมบูรณ์ ซึ่งจะมอบการป้องกันแบบหลายชั้น และคุณจะยังใช้การรองรับเบราว์เซอร์สำหรับผู้ใช้ทุกคนไม่ได้ เว้นแต่คุณจะควบคุมไคลเอ็นต์ของผู้ใช้ได้ (เช่น สภาพแวดล้อมขององค์กร) สักวันหนึ่ง… แต่ตอนนี้ Sandboxing เป็นการป้องกันอีกชั้นหนึ่งเพื่อเสริมการป้องกันของคุณ แต่ไม่ใช่การป้องกันที่สมบูรณ์ซึ่งคุณพึ่งพาได้เพียงอย่างเดียว แต่เลเยอร์ก็ยังคงยอดเยี่ยม เราขอแนะนำให้ใช้ตัวเลือกนี้

อ่านเพิ่มเติม

  • "การแยกสิทธิ์ในแอปพลิเคชัน HTML5" เป็นบทความที่น่าสนใจซึ่งอธิบายการออกแบบเฟรมเวิร์กขนาดเล็กและการใช้งานกับแอป HTML5 ที่มีอยู่ 3 แอป

  • แซนด์บ็อกซ์จะมีความยืดหยุ่นมากขึ้นเมื่อใช้ร่วมกับแอตทริบิวต์ iframe ใหม่อีก 2 รายการ ได้แก่ srcdoc และ seamless รายการแรกช่วยให้คุณป้อนข้อมูลเฟรมด้วยเนื้อหาได้โดยไม่ต้องมีค่าใช้จ่ายเพิ่มเติมสำหรับคำขอ HTTP ส่วนรายการที่ 2 ช่วยให้สไตล์ไหลเข้าสู่เนื้อหาในเฟรมได้ ขณะนี้ทั้ง 2 รายการรองรับเบราว์เซอร์ได้ไม่ดีนัก (Chrome และ WebKit แบบ Nightly) แต่จะเป็นชุดค่าผสมที่น่าสนใจในอนาคต เช่น คุณอาจทดสอบความคิดเห็นในบทความผ่านโค้ดต่อไปนี้

        <iframe sandbox seamless
                srcdoc="<p>This is a user's comment!
                           It can't execute script!
                           Hooray for safety!</p>"></iframe>