前端水印与防篡改策略设计
场景题:
“公司的敏感数据展示后台常常发生:某位内鬼员工用手机偷偷拍下了屏幕或者截图泄漏机密,造成巨大商业灾难。
要求所有包含隐私、财务和敏感资料页面的正中心背景必须悬浮交织一层透明且无法被删改去抹的人员工号水印(Watermark)。这个水印一旦被恶意前端用户从开发者工具里删掉,也会疯狂瞬间复原。”
这是一道防患于未然的安全大关。考点主要是利用 Canvas 生成纹理平铺以及使用 MutationObserver 反制人工黑客破坏。
方案一:初级防御 - 利用 Canvas 生成水印
你完全可以在每一个数据图表或者页面上垫一个写满文字的 <div> 并把透明度设得极低 opacity: 0.1,但这需要你造出无数个冗余 DOM,在海量的管理页面中吃光性能并且维护异常丑陋。
真正的魔法是用 Canvas 原生在后台画出一块极小的(例如 200×200 像素)含有偏转工号字体图案的画布。然后利用 canvas.toDataURL() 把画布转成 base64 图片格式,直接赋予给顶级全屏遮罩容器的 background-image 并指定为 repeat 平铺。因为只有一个底布,极其高效干净。
// 简单提炼的核心代码思路
function createWaterMark(text) {
const canvas = document.createElement('canvas');
canvas.width = 300;
canvas.height = 300;
const ctx = canvas.getContext('2d');
// 画图前把画笔做斜切 -20度旋转赋予高级视觉效果
ctx.rotate((-20 * Math.PI) / 180);
ctx.font = '16px Microsoft YaHei';
ctx.fillStyle = 'rgba(200, 200, 200, 0.40)'; // 半透明极浅色
ctx.fillText(text, 50, 150);
// 用一个穿透层容器盖在最高处!并吸附在视口表面
const markerDiv = document.createElement('div');
markerDiv.id = 'security-watermark-layer';
// 【极重要的属性】让用户的鼠标点击或者划动框选事件可以直接穿透这个遮罩板抓取底层内容,而不是废掉页面交互!
markerDiv.style.pointerEvents = 'none';
markerDiv.style.position = 'fixed';
markerDiv.style.top = '0';
markerDiv.style.left = '0';
markerDiv.style.width = '100vw'; // 永驻全屏范围
markerDiv.style.height = '100vh';
markerDiv.style.zIndex = '9999999';
// 这句话实现了全屏交织密布覆盖平铺的奇观
markerDiv.style.backgroundImage = `url(${canvas.toDataURL('image/png')})`;
document.body.appendChild(markerDiv);
}
方案二:终极绝对防御 - 监控抗造技术 (MutationObserver)
有技术基础的员工(也懂前端),他如果要截图,他只要按下 F12 打开 Chrome 开发者控制台,点击选中你那层辛辛苦苦造出来的水印遮罩层,按下键盘的 Delete 键就能删除 DOM!然后舒心地裸奔截图。
道高一尺魔高一丈。
浏览器在底座提供了一个专门监视并且具有最顶格宏事件监听特权的保安员:MutationObserver。他能够对指定 DOM 的每一次生老病死属性变动立刻做出惊爆响应警报。
核心监控思路:
用监听器盯着水印层的父骨架 body (或者是他自己)。
如果内鬼在控制台妄图执行:
- 直接执行删除了节点操作? -> 在捕捉到树倒下时立刻再重生
appendChild 调用回写生成水印代码。
- 试着通过修改
style="display: none" 或强行把 opacity 降为 0 把这个蒙层隐身掉? -> 捕获到属性变动时,立刻回滚属性再次强打回透明度!
function protectWatermark(targetNode) {
const observer = new MutationObserver((mutationsList) => {
// 这代表有人在暗中搞鬼变动了底盘,不管他是试图移除孩子,还是改孩子的属性
for(let mutation of mutationsList) {
if (mutation.type === 'childList') {
const removedStr = Array.from(mutation.removedNodes).map(n => n.id);
if (removedStr.includes('security-watermark-layer')) {
console.warn('警告!侦测到恶意员工试图清空删除水印!强制拦截回写...');
observer.disconnect(); // 防止在强加时互相死循环触发
createWaterMark('员工工号: 0256221'); // 大招轰炸强制生成塞进去!
protectWatermark(document.body); // 继续启动监听死磕
return;
}
} else if (mutation.type === 'attributes') {
// 有人试图玩阴的改样式了!在这里可以具体匹配样式属性并拦截强制改回
if (mutation.target.id === 'security-watermark-layer') {
// 简单粗暴点:你敢改样式我就敢重新删了建个新的干净的。
}
}
}
});
// 全能开眼模式扫描!盯着 body 下直接孩子的新增移除以及本身特征的变化
observer.observe(targetNode, {
attributes: true,
childList: true,
subtree: true
});
}
面试深水区追问(核武器)
如果面试官是一个深度专家黑客级,他甚至会问:“既然我知道你在用 MutationObserver 重建防卫,如果内鬼用更高级的外挂直接把你的外层包裹生成函数强行利用 Chrome Extensions 中断掉或者篡改环境,防无可防,有什么极其底层的策略解决隐匿追踪?”
面对这道题,说出盲水印(Blind Watermark / 频域加水印)。
盲水印是普通眼睛看不见的!利用傅里叶等图像算法频域重构建,我们将特定编码极度隐秘地打入网页图片的像素和噪点间!甚至用户在截屏截出的图片看仍然是普通的商品页面图!毫无防备的发给流向暗网黑市。
但公司法务一旦截获了这张图,利用解密的傅里叶反向解析代码读取该图片,图片上的工号隐秘编码会在算法显影里暴露得淋漓尽致,精准定位这图是谁在何时通过客户端偷拿。展现不可一世的技术视野深度。