嫦美找到我时,整个人是崩溃的 ------ "卡颂,我好像被监视了"。
傍晚的星巴克,她的影子被吊灯拉得很长,颤抖着如同她此刻的内心。
"怎么回事?"我尽量让声音听起来平静些。
"最近认识个男生,是我MBA同学,对我很热情,也很懂我"嫦美环顾四周,仿佛随时会有什么东西从夜色中跳出来。
"缘分啊,这不很好嘛?"我笑着说。
"不是那种心有灵犀的懂,是那种生活起居都被监视的懂"嫦美解释道。不待我回应,又补充道:"这次约你出来,也是想让你帮忙看看我电脑有没有被植入啥监听木马"。
说罢,从背包里取出MacBook Air
递给我。
"Mac一般安全性都蛮高的,你最近没装啥来路不明的应用吧?"一边摆弄她的电脑,我一边问道。
"我也不会装应用,平时主要就上上网、刷刷剧"。
浏览完她的应用列表,我顺手打开了浏览器,又习惯性打开插件列表。
这时,一个浏览器插件吸引了我的注意:
"这是啥?"
"奥,我们MBA的网课需要在这个平台看。这个平台很严,看课不能快进,也不能切换到其他页面。这是那个男同学发我的,装了后就能突破这些限制,还挺方便"说罢,嫦美皱了皱眉"和这个插件不会有关系吧?"
"不好说,等我看看插件源码"。
事实证明,这个插件真的有问题......
浏览器插件能做什么?
浏览器插件为我们上网提供了极大便利,比如:
-
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
面板看不到插件发出的请求:
都有哪些有价值的数据可以收集呢?
用户敏感数据
最简单的,监听用户键盘输入:
js
[...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
:
js
const tabs = await chrome.tabs.query({
// 筛选用户当前没使用的Tab
active: false,
// 筛选用户没有pin的Tab,pin的Tab使用频率通常比较高
pinned: false,
// 不使用有音频的Tab
audible: false,
// 使用已经加载完毕的Tab
status: "complete",
});
// 筛选出闲置Tab
const [idleTab] = tabs.filter(/** ...省略其他筛选条件 **/)
只要恶意网站的标题、图标(favicon)与闲置Tab一致,那么用恶意网站替换闲置Tab后,用户也不会有任何察觉。
举个例子,如果闲置Tab
是React官网
,那恶意网站只需要标题是React
,图标是React
,即使闲置Tab
跳转到恶意网站,从Tab
外观上也无法区分。
下面的代码构造了恶意网站的url
,其中与闲置Tab一致的标题、图标 保存在url searchParams
中:
js
// 将标题、图标保存在searchParams中
const searchParams = new URLSearchParams({
returnUrl: idleTab.url,
faviconUrl: idleTab.favIconUrl || "",
title: idleTab.title || "",
});
const url = `${chrome.runtime.getURL(
"恶意网站.html"
)}?${searchParams.toString()}`;
恶意网站在url searchParams
中取出标题、图标数据,并替换:
js
// 修改标题
document.title = searchParams.get('title);
// 修改图标
document.querySelector(`link[rel="icon"]`)
.setAttribute("href", searchParams.get('faviconUrl'));
最后,用恶意网站替换闲置Tab
的网站:
js
await chrome.tabs.update(idleTab.id, {
url,
active: false,
});
恶意网站只需要在做完坏事后 或用户重新点击 闲置Tab 时跳回原来的网站即可。代码如下:
js
const searchParams = new URL(window.location.href).searchParams;
function useReturnUrl() {
// 跳回原来网站
window.location.href = searchParams.get('returnUrl');
}
if (document.visibilityState === "visible") {
useReturnUrl();
}
// 用户访问了闲置Tab
document.addEventListener("visibilitychange", () => useReturnUrl());
// ...开始做坏事
// 做完坏事,跳回原来网站
useReturnUrl();
从用户的视角看,当他点击闲置Tab
时,网站重新加载。对于一个闲置的Tab
来说,重新访问时加载页面是再正常不过的逻辑。
只是用户不会知道,这并不是网站重新加载 ,而是退回到前一个网站。
后记
有人会说 ------ 我只使用那些信得过的插件。
但今天信得过的插件,明天就一定信得过么?在暗网中,用户量大的免费浏览器插件能卖不错的价钱。
为什么会有人收购这类没有商业价值的免费插件呢?一种可能是 ------ 收购后向代码中投毒,只要用户升级插件就会中招。
所以,好用的插件不一定没问题,今天没问题的插件明天也不一定没问题。
对于嫦美来说,技术上能做的只能是删除插件、清除缓存、清除cookie
,退出所有的账号登录并修改密码。
但似乎更大的危险,来自现实世界......