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

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

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

Sandboxing ทำงานตามรายการที่อนุญาต เราจะเริ่มต้นด้วยการนําสิทธิ์ทั้งหมดออก จากนั้นเปิดใช้ความสามารถแต่ละรายการอีกครั้งด้วยการเพิ่ม Flag ที่เฉพาะเจาะจงในการกําหนดค่าของ Sandbox สำหรับวิดเจ็ต 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>