写点什么

有点害怕,浏览器插件风险这么多?

作者:高端章鱼哥
  • 2024-03-12
    福建
  • 本文字数:2644 字

    阅读完需:约 9 分钟

有点害怕,浏览器插件风险这么多?

浏览器插件能做什么?

浏览器插件为我们上网提供了极大便利,比如:

  • GPT插件能帮我们一键总结网页内容

  • 翻译插件能实时翻译网页内容

  • 去广告插件能去掉网页牛皮癣,还我们清爽的页面

实际上,浏览器插件除了能分析并修改原始页面外,还能:

  • 获取我们的实时位置

  • 读取、修改我们复制粘贴的内容

  • 读取cookie、浏览历史

  • 屏幕截图

  • 记录键盘输入

  • 等等

可以说,有心人只要利用得当,就能通过浏览器插件获得我们上网的所有足迹。

这时,有人会说:“插件能做这些没错,但必须申请必要的权限,我不给他权限不就行了?”

事实真的这么简单么?

安全约束够么?

《Building Browser Extensions》一书作者 Matt Frisbie 为了演示浏览器插件的潜在安全问题,构造了一个会申请全部 49 项权限的 chrome 浏览器插件spy-extension

当你在浏览器安装这个插件后,浏览器确实会提示你插件申请的权限



不过,等等!明明申请了 49 项权限,这里为什么只显示 5 项?原来,窗口显示的内容行数有限,超出部分需要拖动滚动条才会显示。

可是,又有几个用户会发现在申请的 5 项权限下面,滚动条后面还隐藏了 44 项权限呢

一旦有了权限,想做什么就取决于插件作者的想象力了。可以被用来做坏事的WebExtensions API非常多,比如:

Service Worker

后台运行的Service Worker可以监听发出的网络请求,并在请求发送到网络之前修改它们。

这意味着插件可以使用Service Worker发送数据到服务器,或者在用户浏览网页时拦截请求并发送额外的数据。

由于Service Worker运行于一个独立的后台进程中,所以打开调试工具的Network面板看不到插件发出的请求:



都有哪些有价值的数据可以收集呢?

用户敏感数据

最简单的,监听用户键盘输入:

[...document.querySelectorAll("input,textarea,[contenteditable]")].map((input) =>  input.addEventListener("input", _.debounce((e) => {    // 处理 用户输入  }, 1000)));
复制代码

除此之外:

  • chrome.cookies.getAll({})会以数组的形式返回浏览器的所有cookie

  • chrome.history.search({ text: "" })会以数组形式返回用户的整个浏览历史记录

  • chrome.tabs.captureVisibleTab()会静默将用户当前正在查看的页面截图,并以data URL的形式返回。

  • chrome.webRequest可以让插件监控所有Tab的流量

上述API结合Service Worker传输数据,用户在插件作者面前无异于裸奔。

更高阶的玩法

据嫦美表示 —— 她那个 MBA 同学好像知道她住哪儿,这是怎么做到的呢?很有可能是通过获取地理位置的插件功能。

一个网课插件获取地理位置,这不是太奇怪了么?可是嫦美一点都没发觉,这是怎么办到的?

如果插件脚本获取地理位置(通过navigator.permissions.query({ name: "geolocation" })),将询问用户授权。

但如果被注入脚本的网站已经获得用户的地理位置授权,插件不需要授权就能静默使用对应功能。

举个例子,如果百度地图向你请求获取地理位置的授权,这很合理,你也大概率会同意。

如果恶意插件可以向百度地图注入脚本,当你访问百度地图时,他就不用再获取授权就能访问你的地理位置。

借尸还魂之法

以上所说的所有功能都局限在 —— 插件向已有网站注入脚本。那插件是否能不被察觉的直接打开恶意网站呢?

答案是 —— 可以,我愿称其为借尸还魂之法。

很多朋友都会打开多个浏览器Tab,但常用的可能就是其中几个,剩下的Tab会闲置很长时间。

而这些闲置的 Tab 就是最好的下手目标。


经常打开很多Tab


首先,插件通过以下代码筛选出闲置的Tab

const tabs = await chrome.tabs.query({  // 筛选用户当前没使用的Tab  active: false,  // 筛选用户没有pin的Tab,pin的Tab使用频率通常比较高  pinned: false,  // 不使用有音频的Tab  audible: false,  // 使用已经加载完毕的Tab  status: "complete",});
// 筛选出闲置Tabconst [idleTab] = tabs.filter(/** ...省略其他筛选条件 **/)
复制代码

只要恶意网站的标题、图标(favicon)与闲置 Tab 一致,那么用恶意网站替换闲置 Tab 后,用户也不会有任何察觉。

举个例子,如果闲置TabReact官网,那恶意网站只需要标题是React,图标是React,即使闲置Tab跳转到恶意网站,从Tab外观上也无法区分。



下面的代码构造了恶意网站的url,其中与闲置 Tab 一致的标题、图标保存在url searchParams中:

// 将标题、图标保存在searchParams中const searchParams = new URLSearchParams({  returnUrl: idleTab.url,  faviconUrl: idleTab.favIconUrl || "",  title: idleTab.title || "",});
const url = `${chrome.runtime.getURL( "恶意网站.html")}?${searchParams.toString()}`;
复制代码

恶意网站在url searchParams中取出标题、图标数据,并替换:

// 修改标题document.title = searchParams.get('title);
// 修改图标document.querySelector(`link[rel="icon"]`) .setAttribute("href", searchParams.get('faviconUrl'));
复制代码

最后,用恶意网站替换闲置Tab的网站:

await chrome.tabs.update(idleTab.id, {  url,  active: false,});
复制代码

恶意网站只需要在做完坏事后用户重新点击 闲置 Tab 时跳回原来的网站即可。代码如下:

const searchParams = new URL(window.location.href).searchParams;
function useReturnUrl() { // 跳回原来网站 window.location.href = searchParams.get('returnUrl');}
if (document.visibilityState === "visible") { useReturnUrl();}
// 用户访问了闲置Tabdocument.addEventListener("visibilitychange", () => useReturnUrl());
// ...开始做坏事
// 做完坏事,跳回原来网站useReturnUrl();
复制代码

从用户的视角看,当他点击闲置Tab时,网站重新加载。对于一个闲置的Tab来说,重新访问时加载页面是再正常不过的逻辑。

只是用户不会知道,这并不是网站重新加载,而是退回到前一个网站

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
有点害怕,浏览器插件风险这么多?_高端章鱼哥_InfoQ写作社区