Chrome插件安全最佳实践:防止XSS、CSRF攻击
Chrome插件安全最佳实践:防止XSS、CSRF攻击
前言
Chrome 插件运行在浏览器的高权限环境里,content script 又能直接接触网页 DOM,稍不注意就会引入 XSS(跨站脚本)和 CSRF(跨站请求伪造)风险。一旦插件被注入恶意内容,攻击者就能借插件的权限读取页面、伪造请求,后果比普通网页脚本严重得多。插件能拿到的权限比普通网页高,所以同样一行有漏洞的代码,在插件里造成的危害会被放大,这正是我们必须在源头设防的原因。本文用可运行的代码片段,讲清楚两条最实用的防线:对一切外部内容做转义、对所有跨端消息做校验,并以一款多平台视频下载的 Chrome 插件与桌面客户端作为案例背景。读完你应能照着搭出一个基础安全骨架,把最常见的两类漏洞挡在门外。下面每段都配有可直接运行的代码,你把它们放进自己的扩展目录就能看到效果,不必从头造轮子。建议先通读再动手,理解每一道防线的位置,比单纯复制代码更重要。
环境准备
- 浏览器:Chrome 88 及以上(支持 Manifest V3)
- 项目结构:
- manifest.json(声明权限与 CSP)
- background.js(service worker,处理消息)
- content.js(注入页面,负责采集)
- popup.js(弹窗交互)
- 调试:打开
chrome://extensions,开启"开发者模式",加载已解压的目录。
实现步骤
1. 在 manifest 中收紧 CSP(防止注入脚本执行)
MV3 默认禁止远程脚本,但仍建议显式声明内容安全策略,禁止 unsafe-inline 与 eval。这一步是整道防线的基础:哪怕后面某处代码写漏了,CSP 也会拦下动态注入的脚本。
{
"manifest_version": 3,
"name": "安全示例插件",
"version": "1.0.0",
"permissions": ["storage", "downloads"],
"host_permissions": ["<all_urls>"],
"background": { "service_worker": "background.js" },
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self';"
}
}
说明:script-src 'self' 表示只执行插件自身目录下的脚本,杜绝外部脚本注入;这与多平台视频下载类插件"只解析页面已有媒体、不引入第三方脚本"的做法一致。
2. 转义所有外部内容,防止 XSS
插件常把页面采集到的标题、作者名渲染进弹窗。任何来自网页的字符串都必须转义,绝不能直接 innerHTML。下面这个函数覆盖最常见的五个危险字符:
// utils.js —— 防止 XSS 的转义函数
function escapeHtml(str) {
if (typeof str !== 'string') return '';
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// 渲染时一律使用转义后的文本
function renderItem(title) {
const li = document.createElement('li');
li.textContent = title; // textContent 不会解析 HTML,最安全
// 若必须用 innerHTML,则:li.innerHTML = escapeHtml(title);
document.getElementById('list').appendChild(li);
}
要点:优先用 textContent;必须拼 HTML 时,先用 escapeHtml 处理每一个外部变量。别图省事直接拼接,一次疏忽就可能让恶意脚本被执行。不要把整段外部数据直接塞进页面,先取值、再转义、后渲染。
3. 校验跨端消息,防止伪造请求(CSRF 思路)
content script 与 background 之间的消息可能被伪造。background 收到消息时应校验来源并校验数据结构。下面这段把"来源白名单 + 结构校验 + 动作白名单"三件事一次做齐:
// background.js —— service worker
const ALLOWED_URL_PREFIX = ['chrome-extension://'];
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// 1) 校验发送者合法:必须来自本扩展的页面或 content script
if (!sender.url || !ALLOWED_URL_PREFIX.some(p => sender.url.startsWith(p))) {
if (!sender.tab || !sender.tab.url) return; // 未知来源,直接忽略
}
// 2) 校验消息结构,拒绝畸形数据
if (!message || typeof message.type !== 'string') return;
// 3) 动作白名单:只处理已知类型
const HANDLERS = {
SAVE_MEDIA: (msg) => {
if (typeof msg.url !== 'string' || !/^https?:\/\//.test(msg.url)) return;
chrome.downloads.download({ url: msg.url, saveAs: false });
}
};
if (HANDLERS[message.type]) HANDLERS[message.type](message);
});
要点:不信任任何字段,URL 必须复核协议头,动作走白名单,未知类型一律丢弃。宁可多写几行校验,也别给伪造消息留口子。
4. 给敏感操作加一次性令牌(防 CSRF)
对"下载""修改设置"等敏感动作,要求 content script 先申请一次性令牌,令牌不匹配则拒绝:
// 简易令牌:background 生成,content 端回传校验
let token = null;
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === 'GET_TOKEN') {
token = 't_' + Math.random().toString(36).slice(2);
sendResponse({ token });
}
if (msg.type === 'SENSITIVE_ACTION') {
if (msg.token !== token) { sendResponse({ ok: false }); return; }
token = null; // 一次性,用完作废
sendResponse({ ok: true });
}
});
常见问题
Q1:用了 textContent 还需要 escapeHtml 吗?
不需要。textContent 不会解析标签,是最彻底的防 XSS 方式;escapeHtml 是"必须用 innerHTML 拼接"时的兜底。
Q2:host_permissions 给 <all_urls> 会不会太宽?
对需要读取任意页面媒体的下载类插件是必要的,但要配合上面的消息校验,且不要在 content script 里做高危操作。
Q3:service worker 里 token 会丢吗?
会。MV3 的 service worker 可能休眠,内存中的 token 会清空。生产环境应把令牌存到 chrome.storage.session 或桌面客户端侧。
Q4:popup 里的输入要不要也转义?
要。popup 虽然不直连网页,但用户粘贴的内容同样不可信,渲染前一律按外部数据处理。
Q5:光靠 CSP 够不够?
不够。CSP 是最后一道墙,真正的主线仍是“不信任外部输入 + 校验每一条消息”。两者叠加才稳。
Q6:桌面客户端和插件之间传数据要注意什么?
同样要走校验。桌面端只接受来自本扩展插件的消息,且对每条指令做白名单与格式检查,别把本地接口暴露成任意可调用。
Q7:上线前怎么自测安全?
用故意构造的畸形消息去打自己的 background 监听,看是否会被丢弃;再往页面里塞一段带标签的假标题,看弹窗会不会把它当代码执行。两项都扛住,基本就稳了。
扩展阅读
- Chrome 官方文档:Content Security Policy
- Mozilla 开发者文档:Web 安全中关于 XSS 的说明
- PortSwigger:Web 安全学院 CSRF 专题
安全清单速记
把前面的要点压成一张可勾选的清单:转义一切外部输入、优先 textContent;manifest 里收紧 CSP、禁 eval 与远程脚本;每条跨端消息都验来源、验结构、走白名单;敏感动作加一次性令牌;popup 输入同样当不可信处理。五条全勾上,日常使用的 XSS 与 CSRF 风险基本就可控了。
总结
插件的两条安全底线:一是对所有外部内容转义、优先用 textContent;二是对所有跨端消息做来源与结构校验、敏感动作加令牌。把这两点养成习惯,大多数 XSS 与 CSRF 风险都能在源头挡住——对多平台视频下载这类需要接触页面媒体的 Chrome 插件与桌面客户端尤其重要。安全不是上线前加一次开关,而是写每一行代码时都不轻信外部输入。把校验当成习惯,比任何单点防御都可靠。
#Chrome插件开发 #Web安全 #XSS防护 #CSRF防护 #MuxDesk
更多推荐



所有评论(0)