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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

กำลังทำแซนด์บ็อกซ์ 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 ในทำนองเดียวกัน โค้ดที่ประเมินจะโหลดปลั๊กอิน แสดงหน้าต่างป๊อปอัปใหม่ หรือกิจกรรมอื่นๆ ที่น่ารำคาญหรือเป็นอันตรายไม่ได้

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

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

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

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

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

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

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

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

  • การทำแซนด์บ็อกซ์จะมีความยืดหยุ่นมากขึ้นเมื่อใช้ร่วมกับแอตทริบิวต์ iframe ใหม่อีก 2 รายการ ได้แก่ srcdoc และ seamless วิธีแรกช่วยให้คุณสร้างเฟรมที่มีเนื้อหาได้โดยไม่ต้องมีค่าใช้จ่ายเพิ่มเติมสำหรับคำขอ HTTP และวิธีหลังช่วยให้สไตล์ไหลเข้าสู่เนื้อหาที่อยู่ในเฟรม ทั้ง 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>