数据,一直在思考如何让数据更安全的流转和服务于客户,围绕这样的想法,我们做过许多方面的扩展。我们落地了服务端的数据切片支持场景化的设计,实现了基于 JDBC 协议对 SQL 的拦截与切片,实现了在应用层的全链路数据库审计方案和实现,实现了 WEB 端明暗水印和文档水印等等,但这些都是在应用服务端的改造;那么围绕以上服务端的思想产生了在端上做一些事情,分析了集团内部服务,多以 WEB 服务端对客户和用户提供便利的功能和数据使用场景。WEB 服务多以依赖浏览器的存在进行访问,所以就试图把数据安全防护的方案前置到浏览器上,在浏览器上做辅助性的数据探测,可以在研发阶段和测试阶段发现数据使用的安全问题,比如某个接口的返回了明文手机号,某个页面未使用身份信息但是调用的接口返回了等等场景...
所以我们要做一个浏览插件把数据安全防护能力前置到端上实现,它不会影响开发者和测试人员...
第一步说明了我们为什么要做一款这样的小工具,这个章节就是如何去解决上面的问题和想法。
市场上的浏览器 chrome/firefox/safri....从市场上数据分析目前 chrome 浏览器占比份额为 66.93%,基本主导了浏览器市场,排名第二的是 MicrosoftEdge 浏览器。根据这样的一个结果我们优先从 chrome 插件去着手。
我们团队侧重于后端的方向研发,在前端侧的能力相对来说比较弱势特别是在浏览器插件又涉及到一些底层的运转机制等;首先官方文档去了解 chrome 插件开发,明确了目前 chrome 支持的插件版本为 v2 和 v3,其中不好的消息就是 v2 在 2023 年的时候会被后续的版本放弃,意味着 v2 和 v3 要同时进行,所以又去了解了一下版本的差异,两个版本在网络拦截方面有了比较大的改动,后者变成了规则的形式,对拦截方面不是太友好。
要把数据安全部分防护能力前置到浏览器端,意味着要对当前的功能做分析和实现,首先插件能力不能对研发和测试人员产生影响,其次使用要方便。就有了以下的四个点的想法:
第一是 WEB 水印能力
通过对插件机制的了解,可以在安装插件之后,对生效的站点修改 dom 的结构,把水印自动的增加到页面中。
第二个想到的是敏感的数据
应用的敏感数据来源分为三个部分:页面渲染的资源,接口请求的数据,接口返回的数据,围绕这一层的构思,最终定义要实现对页面和请求的拦截。
第三想法是对页面的操作的事件监听
这个的来源于某些站点会提供大量的数据,用户在使用的时候可以直接使用把数据复制到其它数据,这样的操作属于用户个人行为也不是文件的形式,用户在复制的过程中无法第一时间感知是否为敏感,没有办法做到第一时间的追踪和防护。
第四个..其实是额外的延伸
我们目前同时也做数据流动链相关的项目,在项目进行的过程中发现会在前端路由与后端路由的链条缺失,想到浏览器插件的运行机制是可以通过 dom 的对象拿到前端页面的路由,这样的思路就可以填补了这块数据链关系的缺失。
在 chrome 插件的生命周期中,在 background 能监听请求发出、返回等事件,一般能获取的事件如下:
// web 请求监听,最后一个参数表示阻塞式,需单独声明权限:webRequestBlocking
chrome.webRequest.onBeforeRequest.addListener(details => {
// cancel 表示取消本次请求
if(details.type == 'image' || details.type === 'medis') return {cancel: false};
}, {urls: [""]});
//ajax 生命周期开始
chrome.webRequest.onBeforeRequest.addListener(details => {
console.log('onBeforeRequest', details);
}, { urls: ["://.jd.com/**"] });
chrome.webRequest.onBeforeSendHeaders.addListener(details => {
console.log('onBeforeSendHeaders', details)
},
{ urls: ["://.jd.com/**"] },
['blocking', 'requestHeaders', 'extraHeaders']
);
// 可以拦截 ajax
chrome.webRequest.onResponseStarted.addListener(details => {
console.log('onResponseStarted', details);
}, {urls: ["://.jd.com/**"]});
// 请求完成,但是取不到 response 结果
chrome.webRequest.onCompleted.addListener(details => {
console.log('onCompleted', details);
}, {urls: ["://.jd.com/**"]
该方式无法拿到 response 内容,从网上常见的解决方案,是给浏览器插件添加 devtools_page 模块,然后在 devtools 的页面添加 request 结束事件,如下:
chrome.devtools.network.onRequestFinished.addListener(
function(request) {
//request 包含请求响应数据,如:url,响应内容等
//request.request.url 接口 的 url
//request.getContent 接口返回的内容
}
);
但是该方式有个问题,就是想要触发 devtools 的页面内容,需要在页面按 F12 呼出浏览器的控制台,这个体验就很一般,所以 devtools 这种形式更像是为开发人员提供 debug 工具时引入的。
因为上文使用两种方式的局限性,所以考虑重写 xhr,然后在页面加载时进行注入替换,重写 xhr 的方法如下:
(function () {
var open = XMLHttpRequest.prototype.open;
var send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function (body) {
this["hookQuery"] = body;
send.apply(this, arguments);
};
XMLHttpRequest.prototype.open = function () {
this.addEventListener("load", function () {
if (this.responseType === "" || this.responseType === "text") {
window.dispatchEvent(
new CustomEvent("pageScript", {
...
})
);
}
});
open.apply(this, arguments);
};
})()
主要改写了原有 send 方法和 open 方法,在 send 时,将请求的参数记录到 xhr 实体的一个属性里,然后在请求触发并返回(load 事件)后,将请求的参数、响应的返回内容,通过事件发送出去,事件会在 content_scripts 进行监听和处理,content_scripts 部分代码如下:
// 加载重写的 xhr 所在 js 文件,然后注入到当前页面的 document 中,这样业务的页面会加载这段 js
var hook = document.createElement("script");
hook.src = chrome.runtime.getURL("script/hook.js");
hook.onload = function () {
this.remove();
};
(document.head || document.documentElement).appendChild(hook);
// 监听 xhr 发送过来的消息
window.addEventListener(
"pageScript",
function (event) {
// 处理逻辑
},
false
);
第一版的功能以敏感数据防护为主导线,后续会增加数据安全其它的防护和感知能力,从端上协助业务解决数据安全的风险
作者:CCO 体系 郝帅卫
来源:京东云开发者社区