保持安全

扩展程序可以访问浏览器中的特殊特权,从而使它们成为攻击者的目标。如果某个扩展遭到破坏,则该扩展的所有用户都容易受到恶意和有害入侵的攻击。通过采用这些做法,可以确保扩展的安全性及其用户的安全。

保护开发者帐户

扩展代码通过 Google 帐户上传和更新。如果开发人员的帐户遭到入侵,攻击者可能会将恶意代码直接推送给所有用户。通过专门创建开发人员帐户并启用二次身份验证(最好使用安全密钥)来保护这些帐户。

保持组的可选性

如果使用组发布,则将组限制在受信任的开发人员中。不接受来自陌生人的会员要求。

永远不要使用 HTTP

请求或发送数据时,避免使用 HTTP 连接。假定任何 HTTP 连接都具有窃听者或包含修改。始终应首选 HTTPS,因为它具有内置的安全性,可以绕过大多数中间人攻击

要求最低权限

Chrome 浏览器将扩展程序的访问权限限制为 manifest 中已明确请求的权限。扩展应仅通过注册其依赖的 API 和网站来最大程度地减少其权限。 随意的代码应保持最小化。

限制扩展权限会限制潜在攻击者可以利用的内容。

跨域XMLHttpRequest

扩展只能使用 XMLHttpRequest 从自身和权限中指定的域中获取资源。

  1. {
  2. "name": "Very Secure Extension",
  3. "version": "1.0",
  4. "description": "Example of a Secure Extension",
  5. "permissions": [
  6. "https://developer.chrome.com/*",
  7. "https://*.google.com/"
  8. ],
  9. "manifest_version": 2
  10. }

此扩展程序通过在权限中列出 “https://developer.chrome.com/* ” 和 “https://*google.com/” 来请求访问developer.chrome.com 和Google 子域上的任何内容。如果扩展名遭到入侵,它仍然仅具有与符合匹配模式的网站进行交互的权限。攻击者将无法访问“https://user_bank_info.com” 或与“https://malicious_website.com” 进行交互。

限制 manifest 字段

在 manifest 中包含不必要的注册会产生漏洞,并使扩展更可见。将 manifest 字段限制为扩展所依赖的字段并进行特定的字段注册。

Externally Connectable

使用 externally_connectable 字段来声明该扩展程序将与之交换信息的外部扩展程序和网页。 限制扩展与外部连接的来源。

  1. {
  2. "name": "Super Safe Extension",
  3. "externally_connectable": {
  4. "ids": [
  5. "iamafriendlyextensionhereisdatas"
  6. ],
  7. "matches": [
  8. "https://developer.chrome.com/*",
  9. "https://*google.com/"
  10. ],
  11. "accepts_tls_channel_id": false
  12. },
  13. ...
  14. }

Web Accessible Resources

通过在 web_accessible_resources 下使资源可通过 Web 访问,将使网站和攻击者可以检测到扩展。

  1. {
  2. ...
  3. "web_accessible_resources": [
  4. "images/*.png",
  5. "style/secure_extension.css",
  6. "script/secure_extension.js"
  7. ],
  8. ...
  9. }

可用的 Web 访问资源越多,潜在的攻击者就可以利用更多的途径。将这些文件减至最少。

包括明确的内容安全政策

在 manifest 中包含扩展的内容安全性策略,以防止跨站脚本攻击。如果扩展仅从自身加载资源,请注册以下内容:

  1. {
  2. "name": "Very Secure Extension",
  3. "version": "1.0",
  4. "description": "Example of a Secure Extension",
  5. "content_security_policy": "default-src 'self'"
  6. "manifest_version": 2
  7. }

如果扩展需要包括来自特定主机的脚本,则可以包括以下脚本:

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

避免使用可执行 API

可执行代码的 API 应该替换为更安全的方法。

document.write() 和 innerHTML

虽然使用 document.write() 和 innerHTML 动态创建 HTML 元素可能更简单,但它会留下扩展以及该扩展名所依赖的网页,从而使攻击者可以插入恶意脚本。应该手动创建 DOM 节点,并使用innerText 插入动态内容。

  1. function constructDOM() {
  2. let newTitle = document.createElement('h1');
  3. newTitle.innerText = host;
  4. document.appendChild(newTitle);
  5. }

eval()

尽可能避免使用 eval() 来防止攻击,因为eval() 会执行传递给它的任何代码,这可能是恶意的。

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

取而代之的是,更安全,更快的方法,例如JSON.parse()

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

谨慎使用内容脚本

尽管内容脚本生活在一个孤立的世界中,但它们无法免受攻击:

  • 内容脚本是扩展的唯一可以直接与网页交互的部分。因此,恶意网页可能会操纵内容脚本所依赖的 DOM 部分,或者利用 Web 标准行为,例如命名项
  • 为了与网页的 DOM 交互,内容脚本需要在与网页相同的渲染器进程中执行。这使得内容脚本容易受到通过旁通道攻击(例如 Spectre)数据泄漏的威胁,并且如果恶意网页感染了渲染器进程,则很容易被攻击者接管。

敏感的任务应在专用进程中执行,例如扩展程序的后台脚本。避免意外地将扩展特权暴露给内容脚本:

  • 假设来自内容脚本的消息可能是攻击者制作的(例如,验证并清除所有输入并保护您的脚本免受跨站脚本攻击)。
  • 假设发送到内容脚本的任何数据都可能泄漏到网页上。请勿将敏感数据(例如扩展中的密钥,其他 Web 来源的数据,浏览历史记录)发送到内容脚本。
  • 限制可以由内容脚本触发的特权操作的范围。不允许内容脚本触发对任意 URL 的请求或将任意参数传递给扩展 API(例如,不允许将任意 URL传递给 fetch 或 chrome.tabs.create API)。

注册并清理输入

通过将监听器限制为扩展所期望的内容,验证传入数据的发送方并清除所有输入,来保护扩展免受恶意脚本的攻击。

如果扩展期望从外部网站或扩展进行通信,则仅应注册 runtime.onRequestExternal。 始终验证 sender 是否匹配受信任的来源。

  1. // The ID of an external extension
  2. const kFriendlyExtensionId = "iamafriendlyextensionhereisdatas";
  3. chrome.runtime.onMessageExternal.addListener(
  4. function(request, sender, sendResponse) {
  5. if (sender.id === kFriendlyExtensionId)
  6. doSomething();
  7. });

即使是扩展本身通过 runtime.onMessage 事件发出的消息,也应进行仔细检查,以确保MessageSender 并非来自受感染的内容脚本。

  1. chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  2. if (request.allowedAction)
  3. console.log("This is an allowed action.");
  4. });

通过清除用户输入和输出数据(甚至来自扩展本身和批准的源),防止扩展执行攻击者的脚本。 避免使用可执行的 API。

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

关注 微信公众号「程序化思维」 获取最新 Chrome 插件开发教程。

mp_wechat