ปลอดภัย

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

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

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

จำกัดกลุ่มไว้เฉพาะนักพัฒนาซอฟต์แวร์ที่เชื่อถือได้

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

อย่าใช้ HTTP เลย

เมื่อส่งคำขอหรือส่งข้อมูล ให้หลีกเลี่ยงการเชื่อมต่อ HTTP ให้คิดว่าการเชื่อมต่อ HTTP ใดๆ จะมีผู้ดักฟังหรือมีการแก้ไข ควรเลือกใช้ HTTPS เสมอ เนื่องจากมีระบบรักษาความปลอดภัยในตัว ที่หลีกเลี่ยงการโจมตีแบบ Man-in-the-Middle ได้ส่วนใหญ่

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

เบราว์เซอร์ 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() และ innerHTML

แม้ว่าการสร้างองค์ประกอบ 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 ของหน้าเว็บ สคริปต์เนื้อหาต้องทำงานในกระบวนการแสดงผลเดียวกันกับหน้าเว็บ ซึ่งทำให้สคริปต์เนื้อหาเสี่ยงต่อการรั่วไหลของข้อมูลผ่านการโจมตีแบบ Side-Channel (เช่น Spectre) และเสี่ยงต่อการถูกผู้โจมตีบุกยึดหากหน้าเว็บที่เป็นอันตรายบุกรุกกระบวนการแสดงผล

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

  • ให้คิดว่าข้อความจากสคริปต์เนื้อหาอาจถูกผู้โจมตีสร้างขึ้น (เช่น ตรวจสอบและปรับปรุงอินพุตทั้งหมด และปกป้องสคริปต์จากการโจมตีแบบ Cross-Site Scripting)
  • ให้คิดว่าข้อมูลใดก็ตามที่ส่งไปยังสคริปต์เนื้อหาอาจรั่วไหลไปยังหน้าเว็บ อย่าส่งข้อมูลที่ละเอียดอ่อน (เช่น ข้อมูลลับจากส่วนขยาย ข้อมูลจากต้นทางเว็บอื่นๆ ประวัติการท่องเว็บ) ไปยังสคริปต์เนื้อหา
  • จำกัดขอบเขตของการดำเนินการที่มีสิทธิ์พิเศษซึ่งสคริปต์เนื้อหาสามารถทริกเกอร์ได้ ไม่อนุญาตให้ สคริปต์เนื้อหาทริกเกอร์คำขอไปยัง URL ที่กำหนดเองหรือส่งอาร์กิวเมนต์ที่กำหนดเองไปยัง API ของส่วนขยาย (เช่น ไม่อนุญาตให้ส่ง URL ที่กำหนดเองไปยังfetchหรือ chrome.tabs.createAPI)

ลงทะเบียนและปรับปรุงอินพุต

ปกป้องส่วนขยายจากสคริปต์ที่เป็นอันตรายโดยจำกัด 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;');
}