写在后面
大家好,我是凯里,欢送关注我的「博客」,全是好货色。
这篇文章聊到的「前端水印防篡改」办法,原本是想作为一份专利提交给公司的,但我下笔之前去国内的专利检索网站上搜了下,发现截然不同的计划曾经被申请了,是一位字节跳动的哥们在今年年初递交的。
既然坑曾经被占了,那我就不卷了。
但思考到这个计划又简略又管用,在这里分享给大家。
水印的实现计划
前端水印这个需要其实是始终存在的,最高频的场景是公司外部零碎避免信息透露。如果实现放在前端,支流的实现办法分为以下两类:
- SVG / PNG 等图片联合 CSS background 属性
- Canvas
比方关上一个出名的在线素材编辑网站,查看其水印的实现形式,就是第一种。
好家伙,这么多 important
,足以看进去这位写款式的前端对这个水印非常重视了。
这里我只截图了 CSS,对应的 HTML 就是一个指定了背景图片的 div
元素。
另外一种基于 Canvas 实现的水印,也不难理解,间接画就完事了,这里不再赘述。
水印的破解
前端岁月静好,直到被人关上了 Devtools…
一旦关上了浏览器的开发者工具,通过间接批改元素的 CSS 属性,页面上的水印间接破防。
如果是基于 Canvas 画的水印,看起来比拟难搞,但实际上,间接在控制台的元素区域把整个 Canvas 元素暴力删除掉,水印也就不复存在了。
水印防破解
回顾以下方才讲到的水印破解计划,要么是通过批改元素的 CSS 属性要么是间接批改 DOM 构造,所以能不能尝试用爱感召用户让他不要去批改呢?当然不行。人机交互学中有一个准则就是「要用最大的歹意去推测你的用户」。
但认真一想,用户不论是批改 CSS 还是批改 DOM,都必须要关上浏览器的 devtools,是否不让用户关上 devtools 呢?
当然能够。
禁止用户关上 devtools
拿 windows 举例,用户如果要关上控制台无外乎两种路径:
- 键盘 F12
- 鼠标右键后抉择查看元素
这就好办了:
// 阻止 F12 事件
document.addEventListener('keydown', event => {return 123 !== event.keyCode || event.returnValue = false;});
// 阻止鼠标右键事件
document.addEventListener('contextmenu', event => {return event.returnValue = false;});
这下的确能把希图想要关上 devtools 的用户彻底拦住,但有点简略粗犷了,毕竟鼠标右键当前除了「查看元素」还有一些别的浏览器性能,太“一刀切”会挫伤到用户体验。
监听 devtools 的关上事件
遗憾的是,浏览器并没有提供原生的 devtools 关上事件,但咱们能够曲线救国:通关查看浏览器可视区域和浏览器窗口的差值来判断用户是否关上了 devtools。实际上,在 Github 上坐拥 1.5k+ star 的开源解决方案 devtools-detect 就是这么做的。
外围实现也很简略:
const resize = () => {
const threshold = 200;
const width = window.outerWidth - window.innerWidth > threshold;
const height = window.outerHeight - window.innerHeight > threshold;
if (width || height) {console.log('控制台关上了,用户筹备破解水印了!!!');
}
}
resize();
window.addEventListener('resize', resize);
不过,这个计划有一个很大的破绽:它只能用来检测 devtools 在浏览器页面中内嵌关上时的状况,然而当初的浏览器简直都提供了新窗口关上 devtools 的性能,所以这个检测很容易被绕过。
MutationObserver
事已至此,是时候祭出最屌的计划了(我结尾提到的专利计划):基于 MutationObserver 的元素属性变动监测。
The MutationObserver interface provides the ability to watch for changes being made to the DOM tree. It is designed as a replacement for the older Mutation Events feature, which was part of the DOM3 Events specification.
简言之,MutationObserver 能够监测到 DOM 元素上任何属性的变动状况,如有须要,也能够监听其子元素的变动状况。这不就是咱们须要的吗?
当用户通过 devtools 批改了水印元素的属性时,MutationObserver 能够及时地告诉咱们,这样就能在第一工夫复原咱们的水印。有一点须要留神的是,MutationObserver 监听的是元素的属性,即 attributes
,所以咱们的 css 款式该当作为元素的 style
属性内嵌在 HTML 中。
以下代码是本计划的具体实现:
// <h1 style="margin:100px;"> 别改我 </h1>
const options = {
childList: true,
attributes: true,
subtree: true,
attributesOldValue: true,
characterData: true,
characterDataOldValue: true,
}
const reset = (expression = () => {}) => {setTimeout(() => {observer.disconnect();
// 执行复原办法
expression();
observer.observe(h1, options);
}, 0);
}
const callback = (records) => {const record = records[0];
if (record.type === 'attributes' && record.attributeName === 'style') {reset(() => {h1.setAttribute('style', 'margin:100px;');
});
} else if (record.type === 'characterData') {reset(() => {h1.textContent = '别改我'});
}
}
const observer = new MutationObserver(callback);
observer.observe(h1, options);
图为禁止批改 h1 元素的 style 和 textContent,能够间接复制到 IDE 里玩一下。
这里能够间接把 style 的值抽取为一个常量,凡是用户批改了元素的 style 属性,这段代码会主动用方才的固定常量笼罩用户批改后的值,从而就实现了前端水印的防篡改。
最初
欢送各位加我的微信 「K-I2ving」 交个敌人,同时我也能够帮忙内推(深圳范畴内的任何出名公司都可)。
倡议关注我的「博客」,全是好货色。