背景
随着 chrome 浏览器安全机制的升级,原来的 V2 版本中一些有的权限被移除了,尤其明显的就是 content script 的跨域逻辑处理,这里将对跨域调研的详细过程做一个总结
官方文档
隐藏的比较深,放在 In depth: security 中
https://developer.chrome.com/docs/extensions/mv3/xhr/
大概讲了,自动 chrome73 和 chrome83 就开始进行限制了,在此之前没做过限制
注意一个细节:manifest 中的 Content-Security-Policy 要表达的是针对于 popop.html 和 option.html,以及 content script 中请求的域名安全问题,避免插件被攻击用的,如果想解决 web.whatsapp.com facebook.com 等网站不让在 console 私自载入 jquery 脚本,则必须拦截 url 请求,并将 response.header.content-security-policy 中的限制 host 改为* 即可
一个启发点
// 扩展本身的json竟然也可以用ajax进行请求,这种叫同源请求是没有问题的
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
官方说明这种改造的原因:
Content scripts pose a challenge for Site Isolation, because they run in the same Chrome renderer process as the web page they operate on. This means that the renderer process must be allowed to fetch data from any origin for which the extension has permissions, which in many cases is all origins. In such cases, Site Isolation would have less effectiveness when content scripts are present, because a compromised renderer process could hijack the content scripts and request (and thus leak) any data from the origins listed in the extension. (Thankfully, this is not a problem for Spectre attacks, which cannot take control of content scripts. It is a problem if an attacker can exploit a security bug in Chrome's renderer process, though, allowing the attacker to issue arbitrary requests as if they came from the content script.)
翻译成中文的意思是:
content scripts 和 page 使用的是同一个渲染进程,本来渲染进程中,是严格限制未经服务端允许的跨域请求,且很完美,但若为了兼容 content script 可以直接通过渲染进程发出请求,那渲染进程就得有一种能力放开所有的请求 host 的拦截,而这样能力一旦被黑客利用,那浏览器就很危险了。
content scripts 可以跨域请求吗?
可以,条件有两点:
-
服务端允许跨域请求,插件端可以通过 ajax,axios,fetch 以跨域模式进行请求;
-
如果遇到注入的网站采用的是 https 协议,也必须用 https 协议,用 http 会被自动转为 https,造成请求出错
为什么跨域会如此严重?
这个很少有人说的很明白,举个例子如果 A 网站,已经探明了 BCD 网站的请求接口,包括 http 和 websocket 等,那就意味着,A 网站开发者,可以在 A 站中对 BCD 的接口发起请求,而浏览器是自动将 cookie 携带上去发出,这就意味着 A 网站的开发者就可以完全监听 BCD 站的各种信息,甚至 D 网站的有些数据来源于客户端,而 A 可以通过修改用户提交接口的传入参数来恶意搞垮 D 网站,这种行为都不能发生,所以跨域的危害性很大。
为何说 content script 通过 sendMessage 不能直接传 url 呢?
因为恶意网站 A 可以诱导一个拥有“host_permissions”:<all_urls> 权限且该插件拥有大量有户的情 插件 E,网站 A 就可以绕开跨域问题,然后肆无忌惮获取插件 E 用户的所有网站的信息了,这个想想还挺可怕的。
content scripts 替代方案
-
通过 background 进行中转处理;
// background service worker 代码
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.contentScriptQuery == 'fetchUrl') {
// WARNING: SECURITY PROBLEM - a malicious web page may abuse
// the message handler to get access to arbitrary cross-origin
// resources.
fetch(request.url)
.then(response => response.text())
.then(text => sendResponse(text))
.catch(error => ...)
return true; // Will respond asynchronously.
}
});
// content scripts 代码
chrome.runtime.sendMessage(
{contentScriptQuery: 'fetchUrl',
url: 'https://another-site.com/price-query?itemId=' +
encodeURIComponent(request.itemId)},
response => parsePrice(response.text()));