ปลอดภัย

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

ปกป้องบัญชีนักพัฒนาแอป

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

{
  "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() และ teriorHTML

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

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

ลงทะเบียนและตรวจสอบอินพุต

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