1. 引言:为什么 Chrome 扩展是攻击面重灾区
- 扩展生态现状:装机量、权限广度与用户信任度
- 典型安全事件回顾(供应链投毒、恶意更新、账号劫持)
- 本文目标与读者收益:建立可落地的攻防视角
2. Chrome 扩展基础架构速览
- Manifest V2 与 V3 的核心差异
- 扩展的组成:manifest.json、background service worker、content script、popup、options 页面
- 扩展的运行边界:扩展进程、渲染进程、页面上下文三者关系
- 关键概念:isolated world、extension origin、web_accessible_resources
3. 权限模型与攻击面地图
- permissions 与 host_permissions 的区别与风险等级
- optional_permissions 与运行时授权
- 高危权限清单:
<all_urls>、tabs、cookies、debugger、nativeMessaging、scripting - 攻击面地图:manifest 配置面、消息通信面、内容注入面、外部资源面、更新分发面
#mermaid-svg-ryirMY0B6OMERL55{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ryirMY0B6OMERL55 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ryirMY0B6OMERL55 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ryirMY0B6OMERL55 .error-icon{fill:#552222;}#mermaid-svg-ryirMY0B6OMERL55 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ryirMY0B6OMERL55 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ryirMY0B6OMERL55 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ryirMY0B6OMERL55 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ryirMY0B6OMERL55 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ryirMY0B6OMERL55 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ryirMY0B6OMERL55 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ryirMY0B6OMERL55 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ryirMY0B6OMERL55 .marker.cross{stroke:#333333;}#mermaid-svg-ryirMY0B6OMERL55 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ryirMY0B6OMERL55 p{margin:0;}#mermaid-svg-ryirMY0B6OMERL55 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ryirMY0B6OMERL55 .cluster-label text{fill:#333;}#mermaid-svg-ryirMY0B6OMERL55 .cluster-label span{color:#333;}#mermaid-svg-ryirMY0B6OMERL55 .cluster-label span p{background-color:transparent;}#mermaid-svg-ryirMY0B6OMERL55 .label text,#mermaid-svg-ryirMY0B6OMERL55 span{fill:#333;color:#333;}#mermaid-svg-ryirMY0B6OMERL55 .node rect,#mermaid-svg-ryirMY0B6OMERL55 .node circle,#mermaid-svg-ryirMY0B6OMERL55 .node ellipse,#mermaid-svg-ryirMY0B6OMERL55 .node polygon,#mermaid-svg-ryirMY0B6OMERL55 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ryirMY0B6OMERL55 .rough-node .label text,#mermaid-svg-ryirMY0B6OMERL55 .node .label text,#mermaid-svg-ryirMY0B6OMERL55 .image-shape .label,#mermaid-svg-ryirMY0B6OMERL55 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ryirMY0B6OMERL55 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ryirMY0B6OMERL55 .rough-node .label,#mermaid-svg-ryirMY0B6OMERL55 .node .label,#mermaid-svg-ryirMY0B6OMERL55 .image-shape .label,#mermaid-svg-ryirMY0B6OMERL55 .icon-shape .label{text-align:center;}#mermaid-svg-ryirMY0B6OMERL55 .node.clickable{cursor:pointer;}#mermaid-svg-ryirMY0B6OMERL55 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ryirMY0B6OMERL55 .arrowheadPath{fill:#333333;}#mermaid-svg-ryirMY0B6OMERL55 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ryirMY0B6OMERL55 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ryirMY0B6OMERL55 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ryirMY0B6OMERL55 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ryirMY0B6OMERL55 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ryirMY0B6OMERL55 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ryirMY0B6OMERL55 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ryirMY0B6OMERL55 .cluster text{fill:#333;}#mermaid-svg-ryirMY0B6OMERL55 .cluster span{color:#333;}#mermaid-svg-ryirMY0B6OMERL55 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ryirMY0B6OMERL55 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ryirMY0B6OMERL55 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ryirMY0B6OMERL55 .icon-shape,#mermaid-svg-ryirMY0B6OMERL55 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ryirMY0B6OMERL55 .icon-shape p,#mermaid-svg-ryirMY0B6OMERL55 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ryirMY0B6OMERL55 .icon-shape .label rect,#mermaid-svg-ryirMY0B6OMERL55 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ryirMY0B6OMERL55 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ryirMY0B6OMERL55 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ryirMY0B6OMERL55 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 攻击者
manifest 配置面
消息通信面
内容注入面
外部资源面
更新分发面
过度授权 / 权限提升
消息伪造 / 越权调用
XSS / DOM 注入
远程代码 / 资源劫持
供应链投毒
4. 攻击手法实录(一):消息通信与权限越权
chrome.runtime.onMessage未校验 sender 的典型写法- 外部消息
onMessageExternal与externally_connectable配置陷阱 - content script 与页面之间的
postMessage信任边界 - 案例:一个"万能代理"扩展如何被任意网页调用
5. 攻击手法实录(二):内容注入与 XSS
- content script 中
innerHTML拼接用户数据的后果 - 页面 DOM 与 isolated world 的隔离边界是否绝对
web_accessible_resources暴露内部页面导致的信息泄露- CSP 在扩展页面中的实际约束力与绕过思路
6. 攻击手法实录(三):供应链与更新链路
- 开发者账号被盗后的恶意版本推送
- 第三方依赖与打包脚本的隐蔽植入
- 商店审核机制的局限与用户侧感知滞后
- 案例复盘:从提交到下架的时间线
7. 防御实践:扩展开发侧的安全基线
- 最小权限原则落地:按需申请、按域授权
- 消息通信加固:sender 校验、origin 白名单、消息 schema 校验
- 内容注入加固:优先
textContent、使用 Trusted Types - 敏感数据存储:避免
localStorage明文,慎用chrome.storage同步 - 依赖治理:锁定版本、审计构建产物、开启 2FA
javascript
// 消息来源校验示例
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (!sender.id || sender.id !== chrome.runtime.id) return;
if (sender.origin && !ALLOWED_ORIGINS.includes(sender.origin)) return;
if (!isValidSchema(msg)) return;
handle(msg).then(sendResponse);
return true;
});
8. 防御实践:企业侧与用户侧的检测手段
- 企业策略:ExtensionSettings、强制安装白名单、权限审计
- 静态检测:manifest 权限比对、可疑 API 调用扫描
- 动态检测:流量监控、DOM 变更观测、行为基线
- 用户侧自查清单:权限复核、来源确认、异常行为识别
9. 实战演练:搭建一个可控的漏洞复现环境
- 环境准备:本地扩展加载、测试页面、代理抓包
- 复现步骤:从消息伪造到越权读取
- 修复验证:加固前后对比
- 演练注意事项与合规边界
10. 总结与展望
- 攻防要点回顾:权限、通信、注入、供应链四条主线
- Manifest V3 带来的新变化与新风险
- 给开发者与安全团队的行动清单
- 延伸阅读与工具推荐