ระบบส่วนขยายของ Chrome บังคับใช้นโยบายรักษาความปลอดภัยเนื้อหา (CSP) เริ่มต้นที่เข้มงวด
ข้อจำกัดของนโยบายไม่ซับซ้อน กล่าวคือต้องย้ายสคริปต์ออกจากบรรทัดแยกกัน
ต้องแปลงไฟล์ JavaScript, เครื่องจัดการเหตุการณ์ในบรรทัดเพื่อใช้ addEventListener
และ eval()
คือ
ปิดใช้อยู่ แอป Chrome มีนโยบายที่เข้มงวดยิ่งขึ้น และเราค่อนข้างพอใจกับความปลอดภัย
ที่นโยบายเหล่านี้ระบุไว้
อย่างไรก็ตาม เราตระหนักดีว่าไลบรารีต่างๆ ใช้โครงสร้างที่คล้ายกับ eval()
และ eval
เช่น
new Function()
เพื่อการเพิ่มประสิทธิภาพการทำงานและความสะดวกในการแสดงผล ไลบรารีที่มีการเผยแพร่
มีแนวโน้มที่จะติดตั้งใช้งานรูปแบบนี้เป็นพิเศษ แม้ว่าบางเฟรมเวิร์ก (เช่น Angular.js) จะรองรับ CSP อยู่แล้ว แต่เฟรมเวิร์กยอดนิยมหลายเฟรมเวิร์กยังไม่ได้อัปเดตเป็นกลไกที่เข้ากันได้กับโลกที่ไม่มี eval
ของส่วนขยาย ดังนั้นการนำการรองรับฟังก์ชันการทำงานดังกล่าวออกจึงก่อให้เกิดปัญหามากกว่าที่คาดไว้สำหรับนักพัฒนาแอป
เอกสารนี้แนะนำว่าแซนด์บ็อกซ์เป็นกลไกที่ปลอดภัยในการรวมไลบรารีเหล่านี้ไว้ในโครงการของคุณ โดยไม่กระทบต่อความปลอดภัย เราจะใช้คำว่าส่วนขยายตลอดบทความเพื่อความกระชับ แต่แนวคิดนี้ใช้กับแอปพลิเคชันด้วย
เหตุผลที่ควรใช้แซนด์บ็อกซ์
eval
เป็นอันตรายภายในส่วนขยายเนื่องจากโค้ดที่ดำเนินการมีสิทธิ์เข้าถึงทุกอย่างในสภาพแวดล้อมที่มีสิทธิ์ระดับสูงของส่วนขยาย มี API ของ chrome.*
จำนวนมากที่มีประสิทธิภาพซึ่งอาจช่วย
สร้างผลกระทบอย่างมากต่อความปลอดภัยและความเป็นส่วนตัวของผู้ใช้ การขโมยข้อมูลอย่างง่ายๆ ทำให้เราเป็นกังวลน้อยที่สุด
โซลูชันที่เราเสนอคือแซนด์บ็อกซ์ที่ eval
สามารถใช้โค้ดได้โดยไม่ต้องเข้าถึงข้อมูลของส่วนขยายหรือ API ที่มีมูลค่าสูงของส่วนขยาย ไม่มีข้อมูล API ก็ไม่มีปัญหา
เราดำเนินการได้โดยระบุไฟล์ HTML ที่เฉพาะเจาะจงภายในแพ็กเกจส่วนขยายว่าเป็นแบบแซนด์บ็อกซ์
เมื่อใดก็ตามที่โหลดหน้าที่ใช้แซนด์บ็อกซ์ หน้าจะถูกย้ายไปยังต้นทางที่ไม่ซ้ำและถูกปฏิเสธ
เข้าถึง chrome.*
API หากเราโหลดหน้าแซนด์บ็อกซ์นี้ลงในส่วนขยายผ่าน iframe
เราจะสามารถ
ส่งต่อและปล่อยให้ระบบดำเนินการกับข้อความเหล่านั้น และรอให้ระบบส่งกลับ
ผลลัพธ์ กลไกการรับส่งข้อความที่เรียบง่ายนี้ช่วยให้เรามีทุกสิ่งที่จําเป็นเพื่อรวมโค้ดที่ขับเคลื่อนโดย eval
ไว้ในเวิร์กโฟลว์ของส่วนขยายอย่างปลอดภัย
การสร้างและการใช้แซนด์บ็อกซ์
หากคุณต้องการเจาะลึกโค้ดโดยตรง โปรดดูส่วนขยายตัวอย่างแซนด์บ็อกซ์และดู ปิดอยู่ เป็นตัวอย่างการทำงานของ API การรับส่งข้อความขนาดเล็กที่ด้านบนของแถบควบคุม และควรให้ทุกสิ่งที่คุณต้องการในการทำงาน สำหรับคนที่อยากดู ซึ่งเราจะอธิบายเพิ่มเติม ให้ดูตัวอย่างนี้พร้อมกัน
แสดงรายการไฟล์ในไฟล์ Manifest
แต่ละไฟล์ที่ควรเรียกใช้ภายในแซนด์บ็อกซ์ต้องระบุในไฟล์ Manifest ของส่วนขยายด้วยการเพิ่ม
พร็อพเพอร์ตี้ sandbox
ขั้นตอนนี้เป็นขั้นตอนสำคัญและคุณอาจลืมได้ง่ายๆ ดังนั้นโปรดตรวจสอบอีกครั้งว่าไฟล์ที่อยู่ในแซนด์บ็อกซ์แสดงอยู่ในไฟล์ Manifest ในตัวอย่างนี้ เรากำลังใช้แซนด์บ็อกซ์กับไฟล์ที่ชื่อ "sandbox.html" รายการไฟล์ Manifest มีลักษณะดังนี้
{
...,
"sandbox": {
"pages": ["sandbox.html"]
},
...
}
โหลดไฟล์ที่อยู่ในแซนด์บ็อกซ์
ในการสร้างสิ่งที่น่าสนใจกับไฟล์ที่ทำแซนด์บ็อกซ์ เราต้องโหลดไฟล์ในบริบทที่
ก็แก้ไขได้ด้วยโค้ดของส่วนขยาย ที่นี่ sandbox.html โหลดลงในหน้าเหตุการณ์ (eventpage.html) ของส่วนขยายผ่าน iframe
eventpage.js มีโค้ด
ซึ่งจะส่งข้อความไปยังแซนด์บ็อกซ์เมื่อใดก็ตามที่มีการคลิกการทำงานของเบราว์เซอร์ โดยการค้นหา iframe
ในหน้า และเรียกใช้เมธอด postMessage
ใน contentWindow
ข้อความคือออบเจ็กต์ที่มีพร็อพเพอร์ตี้ 2 รายการ ได้แก่ context
และ command
เราจะเจาะลึกเกี่ยวกับทั้งสองนี้ในอีกสักครู่
chrome.browserAction.onClicked.addListener(function() {
var iframe = document.getElementById('theFrame');
var message = {
command: 'render',
context: {thing: 'world'}
};
iframe.contentWindow.postMessage(message, '*');
});
postMessage
API ได้ที่เอกสารประกอบ postMessage
เกี่ยวกับ MDN เนื้อหาค่อนข้างสมบูรณ์และควรค่าแก่การอ่าน โดยเฉพาะอย่างยิ่ง โปรดทราบว่าข้อมูลจะส่งต่อกลับมาได้ต่อเมื่อเรียงลำดับได้เท่านั้น ตัวอย่างเช่น ฟังก์ชันไม่เป็นเช่นนั้นกระทำการที่เป็นอันตราย
เมื่อโหลด sandbox.html
ระบบจะโหลดไลบรารี Handlebars และสร้างและคอมไพล์เทมเพลตแบบอินไลน์ในลักษณะที่ Handlebars แนะนำ ดังนี้
<script src="handlebars-1.0.0.beta.6.js"></script>
<script id="hello-world-template" type="text/x-handlebars-template">
<div class="entry">
<h1>Hello, !</h1>
</div>
</script>
<script>
var templates = [];
var source = document.getElementById('hello-world-template').innerHTML;
templates['hello'] = Handlebars.compile(source);
</script>
นี่ไม่ล้มเหลว แม้ว่า Handlebars.compile
จะใช้ new Function
แล้ว แต่ก็ยังมีสิ่งต่างๆ อยู่
ตามที่คาดไว้ และในที่สุดเราก็ได้เทมเพลตที่รวบรวมไว้ใน templates['hello']
ส่งผลลัพธ์กลับ
เราจะทำให้เทมเพลตนี้พร้อมใช้งานโดยการตั้งค่า Listener ข้อความที่ยอมรับคำสั่ง
จากหน้ากิจกรรม เราจะใช้ command
ที่ส่งผ่านมาเพื่อพิจารณาสิ่งที่ควรทำ (คุณอาจจินตนาการได้ว่าจะทำมากกว่าแค่การแสดงผล เช่น สร้างเทมเพลต หรือจะจัดการด้วยวิธีใดก็ได้) และระบบจะส่ง context
ไปยังเทมเพลตโดยตรงเพื่อแสดงผล HTML ที่แสดงผล
จะถูกส่งกลับไปยังหน้ากิจกรรมเพื่อให้ส่วนขยายสามารถทำสิ่งที่มีประโยชน์กับคุณในภายหลัง
<script>
window.addEventListener('message', function(event) {
var command = event.data.command;
var name = event.data.name || 'hello';
switch(command) {
case 'render':
event.source.postMessage({
name: name,
html: templates[name](event.data.context)
}, event.origin);
break;
// case 'somethingElse':
// ...
}
});
</script>
กลับไปที่หน้ากิจกรรม เราได้รับข้อความนี้ พร้อมกับทำสิ่งที่น่าสนใจกับ html
ซึ่งเราส่งผ่านไป ในกรณีนี้ เราจะแสดงผลผ่านการแจ้งเตือนบนเดสก์ท็อปเท่านั้น แต่คุณใช้ HTML นี้ได้อย่างปลอดภัยเป็นส่วนหนึ่งของ UI ของส่วนขยาย การแทรกผ่าน innerHTML
ไม่ได้ก่อให้เกิดความเสี่ยงด้านความปลอดภัยที่สำคัญ เนื่องจากแม้จะมีการบุกรุกโค้ดในแซนด์บ็อกซ์อย่างสมบูรณ์ผ่านการโจมตีที่ชาญฉลาด แต่ก็ไม่สามารถแทรกสคริปต์หรือเนื้อหาปลั๊กอินที่เป็นอันตรายลงในบริบทส่วนขยายที่มีสิทธิ์ระดับสูงได้
กลไกนี้ทำให้การจัดทำเทมเพลตเป็นเรื่องง่าย แต่แน่นอนว่าไม่จำกัดเพียงเทมเพลต ช่วง โค้ดที่ทำงานนอกกรอบภายใต้นโยบายรักษาความปลอดภัยเนื้อหาที่เข้มงวดสามารถใช้แซนด์บ็อกซ์ได้ ใน ที่จริงแล้ว มักมีประโยชน์กับคอมโพเนนต์แซนด์บ็อกซ์ของส่วนขยายที่จะทำงานได้อย่างถูกต้องตามลำดับ เพื่อจำกัดสิทธิ์ของแต่ละโปรแกรมไว้ในส่วนสิทธิ์ที่มีขนาดเล็กที่สุดที่จำเป็นเพื่อ ดำเนินการได้อย่างถูกต้อง งานนำเสนอเรื่องการเขียนเว็บแอปและส่วนขยาย Chrome ที่ปลอดภัยจาก Google ตัวอย่างที่ดีของการนำเทคนิคเหล่านี้ไปใช้จริงในงาน I/O ปี 2012 และเป็นเวลา 56 นาที