ส่วนขยายมีสิทธิ์เข้าถึงพิเศษภายในเบราว์เซอร์ ซึ่งทำให้ส่วนขยายเป็นเป้าหมายที่น่าดึงดูดใจสำหรับผู้โจมตี หากส่วนขยายถูกบุกรุก ผู้ใช้ ทุกคน ของส่วนขยายนั้นจะเสี่ยงต่อการถูกบุกรุกที่เป็นอันตรายและไม่พึงประสงค์ รักษาความปลอดภัยของส่วนขยายและปกป้องผู้ใช้ด้วยการนำแนวทางปฏิบัติเหล่านี้ไปใช้
ปกป้องบัญชีนักพัฒนาแอป
ระบบจะอัปโหลดและอัปเดตโค้ดส่วนขยายผ่านบัญชี Google หากบัญชีของนักพัฒนาแอปถูกบุกรุก ผู้โจมตีอาจส่งโค้ดที่เป็นอันตรายไปยังผู้ใช้ทุกคนโดยตรง ปกป้องบัญชีเหล่านี้โดย เปิดใช้การตรวจสอบสิทธิ์แบบ 2 ปัจจัย โดยใช้คีย์ความปลอดภัยหากเป็นไปได้
ใช้บทบาทสมาชิกที่เหมาะสม
หากผู้เผยแพร่โฆษณามีสมาชิกหลายคน โปรดตรวจสอบว่าบทบาทที่มอบให้ผู้ใช้แต่ละราย เหมาะสม
อย่าใช้ HTTP
เมื่อขอหรือส่งข้อมูล ให้หลีกเลี่ยงการเชื่อมต่อ HTTP ให้คิดว่าการเชื่อมต่อ HTTP ใดๆ จะมีผู้ดักฟังหรือมีการแก้ไข ควรเลือกใช้ HTTPS เสมอ เนื่องจากมีระบบรักษาความปลอดภัยในตัว ที่หลีกเลี่ยงการโจมตีแบบ Man-in-the-Middle ได้ส่วนใหญ่
ขอสิทธิ์ขั้นต่ำ
เบราว์เซอร์ Chrome จะจำกัดการเข้าถึงสิทธิ์พิเศษของส่วนขยายเฉพาะสิทธิ์ที่ขอไว้อย่างชัดเจนใน ไฟล์ Manifest ส่วนขยายควรลดสิทธิ์ให้เหลือน้อยที่สุดโดยลงทะเบียนเฉพาะ API และเว็บไซต์ที่ส่วนขยายต้องใช้เท่านั้น
การจำกัดสิทธิ์พิเศษของส่วนขยายจะจำกัดสิ่งที่ผู้โจมตีที่อาจเกิดขึ้นสามารถใช้ประโยชน์ได้
การดึงข้อมูลแบบข้ามต้นทาง fetch()
ส่วนขยายจะใช้ fetch() และ XMLHttpRequest() เพื่อรับแหล่งข้อมูลจาก
ส่วนขยายและจากโดเมนที่ระบุในสิทธิ์ได้เท่านั้น โปรดทราบว่าตัวแฮนเดิลการดึงข้อมูลใน Service Worker จะสกัดกั้นการเรียกทั้ง 2 อย่าง
{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"host_permissions": [
"https://developer.chrome.com/*",
"https://*.google.com/*"
],
"manifest_version": 3
}
ส่วนขยายนี้ในตัวอย่างด้านบนขอสิทธิ์เข้าถึงทุกอย่างใน developer.chrome.com และโดเมนย่อยของ Google โดยระบุ "https://developer.chrome.com/*" และ "https://*.google.com/*" ในสิทธิ์ หากส่วนขยายถูกบุกรุก ส่วนขยายก็ยังคงมีสิทธิ์โต้ตอบกับเว็บไซต์ที่เป็นไปตาม
รูปแบบที่ตรงกันเท่านั้น ผู้โจมตีจะมีความสามารถในการเข้าถึง "https://user_bank_info.com" หรือ
โต้ตอบกับ "https://malicious_website.com" ได้อย่างจำกัด
จำกัดช่องไฟล์ Manifest
การรวมคีย์และสิทธิ์ที่ไม่จำเป็นในไฟล์ Manifest จะสร้างช่องโหว่และทำให้ส่วนขยายมองเห็นได้ชัดเจนขึ้น จำกัดช่องไฟล์ Manifest เฉพาะช่องที่ส่วนขยายต้องใช้
เชื่อมต่อภายนอกได้
ใช้ช่อง "externally_connectable" เพื่อประกาศส่วนขยายและหน้าเว็บภายนอกที่
ส่วนขยายจะแลกเปลี่ยนข้อมูลด้วย จำกัดผู้ที่ส่วนขยายสามารถเชื่อมต่อภายนอกได้เฉพาะแหล่งที่เชื่อถือได้
{
"name": "Super Safe Extension",
"externally_connectable": {
"ids": [
"iamafriendlyextensionhereisdatas"
],
"matches": [
"https://developer.chrome.com/*",
"https://*.google.com/*"
],
"accepts_tls_channel_id": false
},
...
}
แหล่งข้อมูลที่เข้าถึงได้จากเว็บ
การทำให้แหล่งข้อมูลเข้าถึงได้จากเว็บภายใต้ "web_accessible_resources" จะทำให้
ส่วนขยายเว็บไซต์และผู้โจมตีตรวจพบได้
{
...
"web_accessible_resources": [
{
"resources": [ "test1.png", "test2.png" ],
"matches": [ "https://web-accessible-resources-1.glitch.me/*" ]
}
]
...
}
ยิ่งมีแหล่งข้อมูลที่เข้าถึงได้จากเว็บมากเท่าใด ผู้โจมตีที่อาจเกิดขึ้นก็ยิ่งมีช่องทางในการใช้ประโยชน์มากขึ้นเท่านั้น เก็บไฟล์เหล่านี้ไว้ให้น้อยที่สุด
รวมนโยบายรักษาความปลอดภัยเนื้อหาที่ชัดเจน
รวมนโยบายรักษาความปลอดภัยเนื้อหาสำหรับส่วนขยายในไฟล์ Manifest เพื่อป้องกันการโจมตีแบบ Cross-Site Scripting หากส่วนขยายโหลดแหล่งข้อมูลจากตัวส่วนขยายเองเท่านั้น ให้ลงทะเบียนดังนี้
{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"content_security_policy": {
"extension_pages": "default-src 'self'"
},
"manifest_version": 3
}
หากส่วนขยายต้องใช้ Web Assembly หรือเพิ่มข้อจำกัดในหน้าที่มีการแซนด์บ็อกซ์ คุณสามารถเพิ่มข้อจำกัดได้ดังนี้
{
"name": "Very Secure Extension",
"version": "1.0",
"description": "Example of a Secure Extension",
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self';",
"sandboxed_pages":"script-src 'self' 'wasm-unsafe-eval'; object-src 'self';"
},
"manifest_version": 3
}
หลีกเลี่ยง document.write() และ innerHTML
แม้ว่าการสร้างองค์ประกอบ HTML แบบไดนามิกด้วย document.write() และ innerHTML อาจทำได้ง่ายกว่า แต่ก็ทำให้ส่วนขยายและหน้าเว็บที่ส่วนขยายต้องใช้เสี่ยงต่อการถูกผู้โจมตีแทรกสคริปต์ที่เป็นอันตราย ให้สร้างโหนด DOM ด้วยตนเองและใช้ innerText เพื่อแทรกเนื้อหาแบบไดนามิกแทน
function constructDOM() {
let newTitle = document.createElement('h1');
newTitle.innerText = host;
document.appendChild(newTitle);
}
ใช้สคริปต์เนื้อหาอย่างระมัดระวัง
แม้ว่าสคริปต์เนื้อหาจะอยู่ในโลกที่แยกจากกัน แต่ก็ไม่ได้ปลอดภัยจากการโจมตี
- สคริปต์เนื้อหาเป็นส่วนเดียวของส่วนขยายที่โต้ตอบกับหน้าเว็บโดยตรง ด้วยเหตุนี้ หน้าเว็บที่เป็นอันตรายจึงอาจจัดการส่วนต่างๆ ของ DOM ที่สคริปต์เนื้อหาต้องใช้ หรือใช้ประโยชน์จากลักษณะการทำงานที่น่าประหลาดใจของมาตรฐานเว็บ เช่น รายการที่มีชื่อ
- หากต้องการโต้ตอบกับ DOM ของหน้าเว็บ สคริปต์เนื้อหาต้องทำงานในกระบวนการแสดงผลเดียวกันกับหน้าเว็บ ซึ่งทำให้สคริปต์เนื้อหาเสี่ยงต่อการรั่วไหลของข้อมูลผ่านการโจมตีแบบ Side-Channel (เช่น Spectre) และเสี่ยงต่อการถูกผู้โจมตีบุกยึดหากหน้าเว็บที่เป็นอันตรายบุกรุกกระบวนการแสดงผล
การดำเนินการที่ใช้ข้อมูลที่ละเอียดอ่อน (เช่น ข้อมูลส่วนตัวของผู้ใช้) หรือ Chrome API ที่มีสิทธิ์เข้าถึงฟังก์ชันของเบราว์เซอร์ควรดำเนินการใน Service Worker ของส่วนขยาย หลีกเลี่ยงการเปิดเผยสิทธิ์พิเศษของส่วนขยายให้กับสคริปต์เนื้อหาโดยไม่ตั้งใจ
- ให้คิดว่าข้อความจากสคริปต์เนื้อหาอาจถูกสร้างขึ้นโดยผู้โจมตี (เช่น ตรวจสอบและปรับปรุงอินพุตทั้งหมด และปกป้องสคริปต์ของคุณจากการโจมตีแบบ Cross-Site Scripting)
- ให้คิดว่าข้อมูลที่ส่งไปยังสคริปต์เนื้อหาอาจรั่วไหลไปยังหน้าเว็บ อย่าส่งข้อมูลที่ละเอียดอ่อน (เช่น ข้อมูลลับจากส่วนขยาย ข้อมูลจากต้นทางเว็บอื่นๆ ประวัติการท่องเว็บ) ไปยังสคริปต์เนื้อหา
- จำกัดขอบเขตของการดำเนินการที่มีสิทธิ์พิเศษซึ่งสคริปต์เนื้อหาสามารถทริกเกอร์ได้ ไม่อนุญาตให้
สคริปต์เนื้อหาทริกเกอร์คำขอไปยัง URL ที่กำหนดเองหรือส่งอาร์กิวเมนต์ที่กำหนดเองไปยัง
ส่วนขยาย API (เช่น ไม่อนุญาตให้ส่ง URL ที่กำหนดเองไปยัง
fetch()หรือchrome.tabs.create()เมธอด)
ลงทะเบียนและปรับปรุงอินพุต
ปกป้องส่วนขยายจากสคริปต์ที่เป็นอันตรายโดยจำกัด Listener เฉพาะสิ่งที่ส่วนขยายคาดหวัง ตรวจสอบผู้ส่งข้อมูลขาเข้า และปรับปรุงอินพุตทั้งหมด
ส่วนขยายควรลงทะเบียนสำหรับ runtime.onMessageExternal ก็ต่อเมื่อคาดว่าจะมีการสื่อสารจากเว็บไซต์หรือส่วนขยายภายนอก ตรวจสอบเสมอว่าผู้ส่งตรงกับแหล่งที่เชื่อถือได้
// The ID of an external extension
const kFriendlyExtensionId = "iamafriendlyextensionhereisdatas";
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id === kFriendlyExtensionId)
doSomething();
});
แม้แต่ข้อความผ่านเหตุการณ์ runtime.onMessage จากส่วนขยายเองก็ควรได้รับการตรวจสอบอย่างละเอียดเพื่อให้แน่ใจว่า MessageSender ไม่ได้มาจาก สคริปต์เนื้อหา ที่ถูกบุกรุก
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.allowedAction)
console.log("This is an allowed action.");
});