Web前端水印方案
时间:2023-07-03 19:30:01 | 来源:网站运营
时间:2023-07-03 19:30:01 来源:网站运营
Web前端水印方案:
前言
为了防止
信息泄露或
知识产权被侵犯,在web的世界里,对于图片文档等增加水印处理是十分有必要的
水印的添加根据环境可以分为两大类,
前端浏览器环境添加和
后端服务环境添加
根据实现方式又可以分为两大类,
显性水印和
数字水印前端方案;
- 服务器运算量、内存的减少,能够快速响应请求
- 安全性较低,有心人很容易通过各种骚操作直接获取到源文件
后端方案- 安全性较高,无法获取到源文件
- 当遇到大文件密集水印,或是复杂水印,占用服务器内存、运算量,请求时间过长
显性水印- 容易处理,算法较为简单
- 攻击者就可以通过裁剪、模糊等操作对水印进行攻击消除,同时显性水印也会破坏图片的完整性
数字水印当然,
优缺点也需要分情况来看,各个方案都拥有自己的优缺点,需要使用者在
安全性、性能之间衡量
没有最好的方案,只有根据环境与需求,使用当前最适合的方案调研
CSDN、本站、微博- 后端直接处理增加水印(前端直接使用
img
标签显示url
) - 暂时只能看到表层右下角水印(用户名)
- 是否添加数字水印(未知)
作者去调研了一下上述三个网站的做法(
水印应该不止所见),都是直接
img
显示
url
当获取到
url
在浏览器下打开的时候,获取到的是
已经添加水印的图片
从这三个网站的特点来看,这种做法是很合适的,因为他们的需要添加水印的资源往往都绑定到了某一个用户上,也就是,
一份原始资源只需要做
一次处理(前后端都可),将其存储之后就无需再次处理
但是这种方式在
某些情况下是却不是最佳的
举个栗子:资源不跟某一个单独的用户绑定,而是一份资源,多个用户查看,需要在每一个用户查看的时候添加用户特有的水印
上述栗子多用于
某些机密文档或者
内部文件,水印的目的在于文档外流的时候可以追究到责任人
故而,添加水印的方法需要根据
环境的不同、
需求的不同来变化
方案总览
由于作者是一个前端工程师,故而,方案将仅会从
前端角度来阐述,但其中的
算法却是可以
通用,可根据需求
只要服务器够强悍,需求以及环境允许,建议所有的添加水印操作在服务端执行
或是如在上传的时候来执行添加水印操作
需要尽量避免传输给前端原始的资源文件
1. 显性水印+DOM元素直接遮盖
- 安全性:Low(可通过禁用元素直接去除水印层)
- 复杂度:Low(简单的DOM操作即可)
- 性能:Low(过多的DOM元素,添加时将会造成一定的卡顿)
将水印文字直接通过一层DOM元素,覆盖到需要添加水印的图片上
并且可以添加两层,一层为
明显水印,其
透明度较高,肉眼可见,一层为
隐藏水印,
透明度极低,肉眼无法分辨,但可以通过一些
处理后续显现出来(以PS为栗子:现在把图片放到PS里面,建一个图层在上面,全部填充为黑色,混合模式选择
颜色加深这一类的(
也就是让亮的更亮,暗的更暗))
这样,在用户截图的之后,就算涂抹掉了明显水印,可由于隐藏水印肉眼无法分辨,简单的
涂抹攻击并不能准确定位到隐藏水印
经过测试,此方法在
屏幕截图时
工作效果较好但是当使用手机等设备
拍照之后,由于引入了过多的噪声,
效果极差(隐藏水印几乎没有任何效果)
由此,给出
部分情况下的解决方案
当对于图片
完整性要求不高(也就是铺满了水印都不介意,只要看清内容即可)的情况,建议增加水印密度,直到只要用户去涂抹水印,就会直接破坏文件到无法阅读的地步
由于
算法较为简单,就是在
矩形范围内循环的按规则添加水印,具体栗子见参考资料(
显性水印+隐藏水印方案)
2. 显性水印+Canvas
- 安全性:Low(可通过Network直接获取到源图片)
- 复杂度:Low(简单的Canvas操作即可)
- 性能:Middle
其算法和
显性水印+DOM元素直接遮盖一样,但其性能
优于方案一,安全性
略高于方案一
性能优于方案一直接通过Canvas绘画,避免了在水印密度较大的情况下大量DOM元素的创建与添加
并且Canvas在
部分环境与浏览器下拥用
GPU加速的功能,故而性能提升较大
安全性略高于方案一用户无法通过浏览器开发者模式禁用元素来去除掉水印遮罩
但依旧可以通过Network来获取到源图片
此方案与方案一也是可以防小白,但却防不了有心人(稍稍学习一下便可以破解)
此方案只是绘制方式与方案一存在差异,大体相同
3. 保护程序+DOM元素直接遮盖
- 安全性:Middle
- 复杂度:Low(简单的DOM操作即可)
- 性能:Low(过多的DOM元素,添加时将会造成一定的卡顿)
上述方案中,将资源绘制在
Canvas
虽是一种可行方案,但对于普通的
DOM
元素(非图片)
虽然有可行方案例如
html2canva
来将
DOM
转化为·
Canvas
,但是实现过于繁杂
并且
DOM
将失去其事件处理响应功能,故而并不推荐这么使用,除非需要保护的资源
没有任何交互在此,可以借鉴
react-watermark-component
这个库的实现原理
使用浏览器新增的
MutationObserver
特性(主流浏览器都已支持,参考资料中有具体文档链接)
用来监视需要保护的
DOM
元素及其子代的更改(包括监视
DOM
及其子代的删减、
Style
的变化,标签属性变化等等),一旦回调函数通知出现了任何更改
我们可以做出提示,提醒用户操作违法,并且删除掉水印,并且
重新生成水印DOM
或者在用户更改了水印
DOM
的时候,将需要显示的保护资源
DOM
一并删除这样便可以做到防止用户删除水印的效果,但是有心人如果执意想要获取数据,完全可以拷贝下来整个已经渲染完成的
HTML
and
CSS
,去除
JS
的保护干扰,故而,此方案只防住了君子和小白
水印DOM
在生成完成之后,需要谨记,不要在代码端出现了主动修改其的行为
由于使用的是观察者模式,在组件被程序销毁的时候,需要谨记断开连接
4. Base64传输
- 安全性:Middle
- 复杂度:Low
- 性能:Middle
我们从方案二可以看出,前端的水印方案
最大的漏洞便是用户可以通过开发者模式直接获取到传输过来的
源文件这就造成了水印只要
稍微使用一些手段便可以破解掉
在这里引入
Base64,将资源文件通过
Base64编码并且通过
request请求返回(或是直接后端保存
Base64)
而对于
Img
来说,
Base64只需要一些小小的的处理就可以在Web中使用(
Base64字符串可以
直接作为
img
的
url
,但建议使用
Js Image
对象,这样避免了暴露原始
URL
到HTML中,
简易栗子可以产看附录)
并且在浏览器环境下文件也可以很方便的通过调用类似于
readAsDataURL
的方法转化为
一定格式的
Base64这样用户便不能直接(
一键)获取到原始文件资料
注意:此处的url
并非是原始的Base64 url
,而是有一定的格式,称之Data URL
(具体解释参照参考资料),其格式为:data:[<mediatype>][;base64],<data>
,具体栗子可以查看附录
至此,我们防住了
小白,并且防住了
稍微有些电脑知识的人,但是我们还是没有
防住有心人(同行老哥)
5. 加料的Base64
- 安全性:Middle
- 复杂度:Middle
- 性能:Middle
由于此方法
不能直接使用普通的三方Base64编码包
故而,在参考资料中放上了Base64编码的原理以及实现代码例子
当阅读完资料之后,便知道,Base64的编码与解码其实不是很复杂,至少对于同行老哥来说
此时我想到了
MD5加密算法中的
加盐(添加随机字符串到明文数据中)操作
我们也可以在
Base64的解码和编码中
制定一套规则,在原始数据中夹杂
一点私货让不清楚规则的人解码之后得到的是
一团糟的数据(私货越多,数据越糟糕)
处理数据可以从
字节码层面处理(从字节码层面可以添加较为复杂的处理逻辑)
也可以从
Base64字符串层面处理(后端只需要存储原始Base64字符串,并且处理起来节约时间)
举个例子在
Base64编码的时候,是以 3 * 8 = 24位编码为 4 个 6位数据代表的字符串
我们可以在原始的 3 * 8 数据之后再添加一个 随机的 8 位数据,亦或是按照一定规律打乱 3 个字节的顺序
在解码的时候首先按照正常的Base64解码得到字节数据,然后
按照编码规律反向再次解码得到正确的字节码
也许有人会说,同行老哥可以通过
直接查看js文件来
我们可以通过
webpack
等工具,在
production环境,开启
压缩代码,并且
开启删除注释,其效果与
代码混淆类似正常人一般是看不懂输出的代码的(失去了语义化命名和注释)
做了这么多操作,终于是将同行老哥
堪堪抵挡住了
6. 傅里叶大法
次方案需要的
专业知识较多,
算法比较复杂作者现在被“关禁闭”在老家,故而无法掏出《信号与系统》来复习知识,这部分只能
后续补全具体的实现以及知识链接我放到了参考资料中,大家可以自行阅读,此方法的鲁棒性以及抗扰性都较高
总结
First of all低透明度隐藏水印、
傅里叶大法都十分依赖于图片本身的像素质量,当时用
手机拍摄图片之后,将会引入大量的噪声,方法将会失效
故而对于较为
重要的文件还是需要
加大明显水印密度来制裁手机拍摄的影响,因为重要文件一般
无需在意文件完整性(举个栗子:机密图片上全是水印,但只需要能看清图片内容即可)
Then并不是最复杂的方法就最好,就比如傅里叶大法,其水印处理时间可能是上述方案中最长的
但对于一些对于
安全度要求不是那么高的情况,就比如本站用户文章中的图片资源,完全没有必要
此处所写的方案都是从一个
前端工程师的角度来看
如果是一个后端工程师,那么很多操作是
完全不需要的
但是一个方案是在前端执行还是后端执行,总结起来就是
it depends根据自己的需求,在性能和安全度之间做一个
权衡参考资料
- 显性水印+隐藏水印方案
- 数字水印
- Base64编码原理
- Base64实现例子
- 傅里叶转换数字水印
- canvas添加水印教程
- Data URL
- Mutationobserver
附录
Data URL 示例
<img src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNTgwODkyMjMyMDU3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjIwNzY0IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiPjxkZWZzPjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+PC9zdHlsZT48L2RlZnM+PHBhdGggZD0iTTIyNi40MDY0IDMyNi43NTg0bS00MC45NiAwYTQwLjk2IDQwLjk2IDAgMSAwIDgxLjkyIDAgNDAuOTYgNDAuOTYgMCAxIDAtODEuOTIgMFoiIHAtaWQ9IjIwNzY1IiBmaWxsPSIjRkY1NzIyIj48L3BhdGg+PHBhdGggZD0iTTgzOC4zNDg4IDQzNC4yNzg0YTIwLjQ4IDIwLjQ4IDAgMCAxLTE0LjQzODQtNi4wNDE2TDczNy4yOCAzNDEuMjk5MmEyMC40OCAyMC40OCAwIDAgMSAwLTI4Ljc3NDRMODI0LjExNTIgMjI1LjI4YTIwLjQ4IDIwLjQ4IDAgMCAxIDI4Ljk3OTIgMjguOTc5MmwtNzIuNjAxNiA3Mi42MDE2TDg1Mi43ODcyIDM5OS4zNmEyMC40OCAyMC40OCAwIDAgMS0xNC40Mzg0IDM0LjkxODR6TTUxMiA4MDQuNzYxNmMtMTUwLjkzNzYgMC0yNzkuNTUyLTEwMS43ODU2LTMwNS45NzEyLTI0MS45NzEyYTIwLjQ4IDIwLjQ4IDAgMCAxIDQwLjI0MzItNy41Nzc2QzI2OS4yMDk2IDY3NS44NCAzODAuOTI4IDc2My44MDE2IDUxMiA3NjMuODAxNlM3NTUuMiA2NzUuODQgNzc4LjI0IDU1NS4yMTI4YTIwLjQ4IDIwLjQ4IDAgMCAxIDM5LjkzNiA3Ljk4NzJDNzkxLjc1NjggNzAyLjk3NiA2NjMuMTQyNCA4MDQuNzYxNiA1MTIgODA0Ljc2MTZ6IiBwLWlkPSIyMDc2NiIgZmlsbD0iI0ZGNTcyMiI+PC9wYXRoPjwvc3ZnPg==">
Data URL to Canvas
function dataURLToCanvas(dataurl, cb){ var canvas = document.createElement('CANVAS'); var ctx = canvas.getContext('2d'); var img = new Image(); img.onload = function(){ canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0); cb(canvas); }; img.src = dataurl;}