ปลอดภัย

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

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

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

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

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

ไม่ใช้ HTTP

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

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

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

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

การดึงข้อมูลข้ามต้นทาง()

ส่วนขยายจะใช้ได้เฉพาะ fetch() และ XMLHttpRequest() เพื่อรับทรัพยากรจากส่วนขยายและจากโดเมนที่ระบุในสิทธิ์ โปรดทราบว่าการเรียกใช้ทั้ง 2 รายการจะถูกขัดจังหวะโดยตัวแฮนเดิล fetch ใน Service Worker

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

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

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

ใช้สคริปต์เนื้อหาอย่างระมัดระวัง

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

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

การดำเนินการโดยใช้ข้อมูลที่ละเอียดอ่อน (เช่น ข้อมูลส่วนตัวของผู้ใช้) หรือ Chrome API ที่มีสิทธิ์เข้าถึงฟังก์ชันของเบราว์เซอร์ควรดำเนินการใน Service Worker ของส่วนขยาย หลีกเลี่ยงการเปิดเผยสิทธิ์ของส่วนขยายแก่สคริปต์เนื้อหาโดยไม่ตั้งใจ

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

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

ส่วนขยายควรลงทะเบียนสำหรับ 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.");
});