ปลอดภัย

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

ปกป้องบัญชีนักพัฒนาซอฟต์แวร์

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

เก็บการเลือกกลุ่มไว้

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

ไม่ใช้ HTTP เลย

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

ขอสิทธิ์ขั้นต่ำ

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

การจำกัดสิทธิ์ของส่วนขยายจะจำกัดสิ่งที่ผู้โจมตีอาจใช้ประโยชน์ได้

XMLHttpRequest แบบข้ามต้นทาง

ส่วนขยายจะใช้ได้เฉพาะ XMLHttpRequest เพื่อรับทรัพยากรจากตัวเองและจากโดเมนที่ระบุไว้ในสิทธิ์เท่านั้น

{
  "name": "Very Secure Extension",
  "version": "1.0",
  "description": "Example of a Secure Extension",
  "permissions": [
    "/*",
    "https://*.google.com/"
  ],
  "manifest_version": 2
}

ส่วนขยายนี้ส่งคำขอเข้าถึงทุกสิ่งใน developer.chrome.com และโดเมนย่อยของ Google โดยระบุ "/*" และ "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://*google.com/"
    ],
    "accepts_tls_channel_id": false
  },
  ...
}

แหล่งข้อมูลที่เข้าถึงได้บนเว็บ

การทำให้เว็บเข้าถึงทรัพยากรได้ภายใต้ web_accessible_resources จะเป็นการทำให้เว็บไซต์และผู้โจมตีตรวจจับส่วนขยายได้

{
  ...
  "web_accessible_resources": [
    "images/*.png",
    "style/secure_extension.css",
    "script/secure_extension.js"
  ],
  ...
}

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

ระบุนโยบายรักษาความปลอดภัยเนื้อหาที่อาจไม่เหมาะสม

ใส่นโยบายรักษาความปลอดภัยเนื้อหาสำหรับส่วนขยายในไฟล์ Manifest เพื่อป้องกันการโจมตีแบบ Cross-site Scripting หากส่วนขยายโหลดทรัพยากรจากตัวเองเท่านั้น ให้ลงทะเบียนรายการต่อไปนี้

{
  "name": "Very Secure Extension",
  "version": "1.0",
  "description": "Example of a Secure Extension",
  "content_security_policy": "default-src 'self'"
  "manifest_version": 2
}

หากจำเป็นต้องรวมสคริปต์จากโฮสต์ที่เฉพาะเจาะจง ส่วนขยายเหล่านั้นก็ใส่สคริปต์ได้ ดังนี้

{
  "name": "Very Secure Extension",
  "version": "1.0",
  "description": "Example of a Secure Extension",
  "content_security_policy": "default-src 'self' https://extension.resource.com"
  "manifest_version": 2
}

หลีกเลี่ยง API ที่ดำเนินการได้

คุณควรแทนที่ API ที่เรียกใช้โค้ดด้วยทางเลือกที่ปลอดภัยกว่า

document.write() และ inlineHTML

แม้ว่าการสร้างองค์ประกอบ HTML แบบไดนามิกด้วย document.write() และ innerHTML อาจทำได้ง่ายกว่า แต่ส่วนขยายจะทิ้งส่วนขยายไว้ และหน้าเว็บที่ส่วนขยายต้องพึ่งพาเครื่องมือดังกล่าว ซึ่งจะเปิดโอกาสให้ผู้โจมตีที่แทรกสคริปต์ที่เป็นอันตรายได้ ให้สร้างโหนด DOM ด้วยตนเองและใช้ innerText เพื่อแทรกเนื้อหาแบบไดนามิกแทน

function constructDOM() {
  let newTitle = document.createElement('h1');
  newTitle.innerText = host;
  document.appendChild(newTitle);
}

eval()

หลีกเลี่ยงการใช้ eval() ทุกครั้งที่ทำได้เพื่อป้องกันการโจมตี เพราะ eval() จะเรียกใช้โค้ดใดๆ ก็ตามที่ส่งต่อเข้ามา ซึ่งอาจเป็นอันตราย

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // WARNING! Might be evaluating an evil script!
    var resp = eval("(" + xhr.responseText + ")");
    ...
  }
}
xhr.send();

แต่แนะนำให้ใช้วิธีการที่ปลอดภัยกว่าและรวดเร็วขึ้นแทน เช่น JSON.parse()

var xhr = new XMLHttpRequest();
xhr.open("GET", "https://api.example.com/data.json", true);
xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    // JSON.parse does not evaluate the attacker's scripts.
    var resp = JSON.parse(xhr.responseText);
  }
}
xhr.send();

ใช้สคริปต์เนื้อหาอย่างรอบคอบ

แม้ว่าสคริปต์เนื้อหาจะอยู่ในโลกที่โดดเดี่ยว แต่ก็ไม่ได้รับภูมิคุ้มกันจากการถูกโจมตี

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

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

ลงทะเบียนและดูแลข้อมูลอินพุต

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

ส่วนขยายควรลงทะเบียนสำหรับ runtime.onRequestExternal เฉพาะในกรณีที่คาดว่าจะได้รับการสื่อสารจากเว็บไซต์หรือส่วนขยายภายนอก ตรวจสอบให้แน่ใจเสมอว่าผู้ส่ง ตรงกับแหล่งที่มาที่เชื่อถือได้

// 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.");
});

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

function sanitizeInput(input) {
    return input.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;');
}