确保安全

扩展程序可获得浏览器内的特殊权限,因此很受攻击者的青睐。如果扩展程序遭到入侵,该扩展程序的每位用户都会受到恶意和不必要的入侵。通过纳入这些做法,可以确保扩展程序及其用户的安全。

保护开发者账号

扩展程序代码通过 Google 账号上传和更新。如果开发者的帐号遭到入侵,攻击者可能会直接向所有用户推送恶意代码。为了保护这些帐号,您可以专门创建开发者帐号并启用双重身份验证(最好使用安全密钥)。

确保群组有选择性

如果使用群组发布功能,请确保群组仅限受信任的开发者使用。请勿接受陌生人的成员资格申请。

从未使用 HTTP

请求或发送数据时,避免使用 HTTP 连接。假设所有 HTTP 连接都有窥探者或包含修改内容。HTTPS 应始终是首选,因为它具有内置安全机制,可以规避大多数中间人攻击

请求最小权限

Chrome 浏览器会限制扩展程序只能访问清单中明确请求的权限。扩展程序应仅注册其依赖的 API 和网站,以尽可能降低其权限。应尽量减少任意代码。

限制扩展程序权限可以限制潜在攻击者可利用的漏洞。

跨域 XMLHttpRequest

扩展程序只能使用 XMLHttpRequest 从自身以及权限中指定的网域获取资源。

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

此扩展程序通过在权限中列出 "/*""https://*google.com/",请求访问 developer.chrome.com 上的所有内容以及 Google 的子网域。如果扩展程序遭到破解,它依然有权与符合匹配模式的网站互动。攻击者将无法访问 "https://user_bank_info.com" 或与 "https://malicious_website.com" 互动。

限制清单字段

在清单中包含不必要的注册会造成漏洞,并使扩展程序更加醒目。将清单字段限制为扩展程序所依赖的字段,并为特定的字段注册。

可外部连接

使用 externally_connectable 字段声明该扩展程序将与哪些外部扩展程序和网页交换信息。限制扩展程序可以在外部连接到可信来源的人员。

{
  "name": "Super Safe Extension",
  "externally_connectable": {
    "ids": [
      "iamafriendlyextensionhereisdatas"
    ],
    "matches": [
      "/*",
      "https://*google.com/"
    ],
    "accepts_tls_channel_id": false
  },
  ...
}

可通过网络访问的资源

将资源设为可通过 Web 访问(在 web_accessible_resources 下),即可让网站和攻击者可检测到相应扩展程序。

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

可供网络访问的资源越多,潜在攻击者利用的途径就越多。尽量减少这些文件的数量。

添加露骨内容安全政策

在清单中添加扩展程序的内容安全政策,以防止跨站脚本攻击。如果扩展程序仅从自身加载资源,请注册以下内容:

{
  "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

虽然使用 document.write()innerHTML 动态创建 HTML 元素可能更简单,但这样会使扩展程序和扩展程序所依赖的网页更容易被攻击者插入恶意脚本。请改为手动创建 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),并在有恶意网页入侵渲染器进程时被攻击者接管。

敏感工作应在专用进程(如扩展程序的后台脚本)中执行。避免意外地向内容脚本公开扩展程序权限:

  • 假设“来自内容脚本的消息”可能是攻击者创建了(例如,验证并清理所有输入,并保护您的脚本免受跨站脚本攻击的影响)。
  • 假设发送到内容脚本的所有数据都可能泄露到网页。请勿将敏感数据(例如,扩展程序中的 Secret、来自其他网站源的数据、浏览记录)发送到内容脚本。
  • 限制内容脚本可以触发的特权操作的范围。不允许内容脚本触发对任意网址的请求,或向扩展程序 API 传递任意参数(例如,不允许将任意网址传递给 fetchchrome.tabs.create API)。

注册并清理输入

将监听器限制为仅扩展程序预期的内容、验证传入数据的发送者并清理所有输入,从而保护扩展程序免受恶意脚本的侵害。

扩展程序应仅在希望与外部网站或扩展程序通信时注册 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;');
}