Web前端水印方案
時(shí)間:2023-07-03 19:30:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-07-03 19:30:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
Web前端水印方案:
前言
為了防止
信息泄露或
知識(shí)產(chǎn)權(quán)被侵犯,在web的世界里,對(duì)于圖片文檔等增加水印處理是十分有必要的
水印的添加根據(jù)環(huán)境可以分為兩大類(lèi),
前端瀏覽器環(huán)境添加和
后端服務(wù)環(huán)境添加
根據(jù)實(shí)現(xiàn)方式又可以分為兩大類(lèi),
顯性水印和
數(shù)字水印前端方案;
- 服務(wù)器運(yùn)算量、內(nèi)存的減少,能夠快速響應(yīng)請(qǐng)求
- 安全性較低,有心人很容易通過(guò)各種騷操作直接獲取到源文件
后端方案- 安全性較高,無(wú)法獲取到源文件
- 當(dāng)遇到大文件密集水印,或是復(fù)雜水印,占用服務(wù)器內(nèi)存、運(yùn)算量,請(qǐng)求時(shí)間過(guò)長(zhǎng)
顯性水印- 容易處理,算法較為簡(jiǎn)單
- 攻擊者就可以通過(guò)裁剪、模糊等操作對(duì)水印進(jìn)行攻擊消除,同時(shí)顯性水印也會(huì)破壞圖片的完整性
數(shù)字水印- 算法一般較為復(fù)雜
- 抗攻擊能力較強(qiáng)
當(dāng)然,
優(yōu)缺點(diǎn)也需要分情況來(lái)看,各個(gè)方案都擁有自己的優(yōu)缺點(diǎn),需要使用者在
安全性、性能之間衡量
沒(méi)有最好的方案,只有根據(jù)環(huán)境與需求,使用當(dāng)前最適合的方案調(diào)研
CSDN、本站、微博- 后端直接處理增加水?。ㄇ岸酥苯邮褂?code>img標(biāo)簽顯示
url
) - 暫時(shí)只能看到表層右下角水印(用戶名)
- 是否添加數(shù)字水?。ㄎ粗?/li>
作者去調(diào)研了一下上述三個(gè)網(wǎng)站的做法(
水印應(yīng)該不止所見(jiàn)),都是直接
img
顯示
url
當(dāng)獲取到
url
在瀏覽器下打開(kāi)的時(shí)候,獲取到的是
已經(jīng)添加水印的圖片
從這三個(gè)網(wǎng)站的特點(diǎn)來(lái)看,這種做法是很合適的,因?yàn)樗麄兊男枰砑铀〉馁Y源往往都綁定到了某一個(gè)用戶上,也就是,
一份原始資源只需要做
一次處理(前后端都可),將其存儲(chǔ)之后就無(wú)需再次處理
但是這種方式在
某些情況下是卻不是最佳的
舉個(gè)栗子:資源不跟某一個(gè)單獨(dú)的用戶綁定,而是一份資源,多個(gè)用戶查看,需要在每一個(gè)用戶查看的時(shí)候添加用戶特有的水印
上述栗子多用于
某些機(jī)密文檔或者
內(nèi)部文件,水印的目的在于文檔外流的時(shí)候可以追究到責(zé)任人
故而,添加水印的方法需要根據(jù)
環(huán)境的不同、
需求的不同來(lái)變化
方案總覽
由于作者是一個(gè)前端工程師,故而,方案將僅會(huì)從
前端角度來(lái)闡述,但其中的
算法卻是可以
通用,可根據(jù)需求
只要服務(wù)器夠強(qiáng)悍,需求以及環(huán)境允許,建議所有的添加水印操作在服務(wù)端執(zhí)行
或是如在上傳的時(shí)候來(lái)執(zhí)行添加水印操作
需要盡量避免傳輸給前端原始的資源文件
1. 顯性水印+DOM元素直接遮蓋
- 安全性:Low(可通過(guò)禁用元素直接去除水印層)
- 復(fù)雜度:Low(簡(jiǎn)單的DOM操作即可)
- 性能:Low(過(guò)多的DOM元素,添加時(shí)將會(huì)造成一定的卡頓)
將水印文字直接通過(guò)一層DOM元素,覆蓋到需要添加水印的圖片上
并且可以添加兩層,一層為
明顯水印,其
透明度較高,肉眼可見(jiàn),一層為
隱藏水印,
透明度極低,肉眼無(wú)法分辨,但可以通過(guò)一些
處理后續(xù)顯現(xiàn)出來(lái)(以PS為栗子:現(xiàn)在把圖片放到PS里面,建一個(gè)圖層在上面,全部填充為黑色,混合模式選擇
顏色加深這一類(lèi)的(
也就是讓亮的更亮,暗的更暗))
這樣,在用戶截圖的之后,就算涂抹掉了明顯水印,可由于隱藏水印肉眼無(wú)法分辨,簡(jiǎn)單的
涂抹攻擊并不能準(zhǔn)確定位到隱藏水印
經(jīng)過(guò)測(cè)試,此方法在
屏幕截圖時(shí)
工作效果較好但是當(dāng)使用手機(jī)等設(shè)備
拍照之后,由于引入了過(guò)多的噪聲,
效果極差(隱藏水印幾乎沒(méi)有任何效果)
由此,給出
部分情況下的解決方案
當(dāng)對(duì)于圖片
完整性要求不高(也就是鋪滿了水印都不介意,只要看清內(nèi)容即可)的情況,建議增加水印密度,直到只要用戶去涂抹水印,就會(huì)直接破壞文件到無(wú)法閱讀的地步
由于
算法較為簡(jiǎn)單,就是在
矩形范圍內(nèi)循環(huán)的按規(guī)則添加水印,具體栗子見(jiàn)參考資料(
顯性水印+隱藏水印方案)
2. 顯性水印+Canvas
- 安全性:Low(可通過(guò)Network直接獲取到源圖片)
- 復(fù)雜度:Low(簡(jiǎn)單的Canvas操作即可)
- 性能:Middle
其算法和
顯性水印+DOM元素直接遮蓋一樣,但其性能
優(yōu)于方案一,安全性
略高于方案一
性能優(yōu)于方案一直接通過(guò)Canvas繪畫(huà),避免了在水印密度較大的情況下大量DOM元素的創(chuàng)建與添加
并且Canvas在
部分環(huán)境與瀏覽器下?lián)碛?b>GPU加速的功能,故而性能提升較大
安全性略高于方案一用戶無(wú)法通過(guò)瀏覽器開(kāi)發(fā)者模式禁用元素來(lái)去除掉水印遮罩
但依舊可以通過(guò)Network來(lái)獲取到源圖片
此方案與方案一也是可以防小白,但卻防不了有心人(稍稍學(xué)習(xí)一下便可以破解)
此方案只是繪制方式與方案一存在差異,大體相同
3. 保護(hù)程序+DOM元素直接遮蓋
- 安全性:Middle
- 復(fù)雜度:Low(簡(jiǎn)單的DOM操作即可)
- 性能:Low(過(guò)多的DOM元素,添加時(shí)將會(huì)造成一定的卡頓)
上述方案中,將資源繪制在
Canvas
雖是一種可行方案,但對(duì)于普通的
DOM
元素(非圖片)
雖然有可行方案例如
html2canva
來(lái)將
DOM
轉(zhuǎn)化為·
Canvas
,但是實(shí)現(xiàn)過(guò)于繁雜
并且
DOM
將失去其事件處理響應(yīng)功能,故而并不推薦這么使用,除非需要保護(hù)的資源
沒(méi)有任何交互在此,可以借鑒
react-watermark-component
這個(gè)庫(kù)的實(shí)現(xiàn)原理
使用瀏覽器新增的
MutationObserver
特性(主流瀏覽器都已支持,參考資料中有具體文檔鏈接)
用來(lái)監(jiān)視需要保護(hù)的
DOM
元素及其子代的更改(包括監(jiān)視
DOM
及其子代的刪減、
Style
的變化,標(biāo)簽屬性變化等等),一旦回調(diào)函數(shù)通知出現(xiàn)了任何更改
我們可以做出提示,提醒用戶操作違法,并且刪除掉水印,并且
重新生成水印DOM
或者在用戶更改了水印
DOM
的時(shí)候,將需要顯示的保護(hù)資源
DOM
一并刪除這樣便可以做到防止用戶刪除水印的效果,但是有心人如果執(zhí)意想要獲取數(shù)據(jù),完全可以拷貝下來(lái)整個(gè)已經(jīng)渲染完成的
HTML
and
CSS
,去除
JS
的保護(hù)干擾,故而,此方案只防住了君子和小白
水印DOM
在生成完成之后,需要謹(jǐn)記,不要在代碼端出現(xiàn)了主動(dòng)修改其的行為
由于使用的是觀察者模式,在組件被程序銷(xiāo)毀的時(shí)候,需要謹(jǐn)記斷開(kāi)連接
4. Base64傳輸
- 安全性:Middle
- 復(fù)雜度:Low
- 性能:Middle
我們從方案二可以看出,前端的水印方案
最大的漏洞便是用戶可以通過(guò)開(kāi)發(fā)者模式直接獲取到傳輸過(guò)來(lái)的
源文件這就造成了水印只要
稍微使用一些手段便可以破解掉
在這里引入
Base64,將資源文件通過(guò)
Base64編碼并且通過(guò)
request請(qǐng)求返回(或是直接后端保存
Base64)
而對(duì)于
Img
來(lái)說(shuō),
Base64只需要一些小小的的處理就可以在Web中使用(
Base64字符串可以
直接作為
img
的
url
,但建議使用
Js Image
對(duì)象,這樣避免了暴露原始
URL
到HTML中,
簡(jiǎn)易栗子可以產(chǎn)看附錄)
并且在瀏覽器環(huán)境下文件也可以很方便的通過(guò)調(diào)用類(lèi)似于
readAsDataURL
的方法轉(zhuǎn)化為
一定格式的
Base64這樣用戶便不能直接(
一鍵)獲取到原始文件資料
注意:此處的url
并非是原始的Base64 url
,而是有一定的格式,稱(chēng)之Data URL
(具體解釋參照參考資料),其格式為:data:[<mediatype>][;base64],<data>
,具體栗子可以查看附錄
至此,我們防住了
小白,并且防住了
稍微有些電腦知識(shí)的人,但是我們還是沒(méi)有
防住有心人(同行老哥)
5. 加料的Base64
- 安全性:Middle
- 復(fù)雜度:Middle
- 性能:Middle
由于此方法
不能直接使用普通的三方Base64編碼包
故而,在參考資料中放上了Base64編碼的原理以及實(shí)現(xiàn)代碼例子
當(dāng)閱讀完資料之后,便知道,Base64的編碼與解碼其實(shí)不是很復(fù)雜,至少對(duì)于同行老哥來(lái)說(shuō)
此時(shí)我想到了
MD5加密算法中的
加鹽(添加隨機(jī)字符串到明文數(shù)據(jù)中)操作
我們也可以在
Base64的解碼和編碼中
制定一套規(guī)則,在原始數(shù)據(jù)中夾雜
一點(diǎn)私貨讓不清楚規(guī)則的人解碼之后得到的是
一團(tuán)糟的數(shù)據(jù)(私貨越多,數(shù)據(jù)越糟糕)
處理數(shù)據(jù)可以從
字節(jié)碼層面處理(從字節(jié)碼層面可以添加較為復(fù)雜的處理邏輯)
也可以從
Base64字符串層面處理(后端只需要存儲(chǔ)原始Base64字符串,并且處理起來(lái)節(jié)約時(shí)間)
舉個(gè)例子在
Base64編碼的時(shí)候,是以 3 * 8 = 24位編碼為 4 個(gè) 6位數(shù)據(jù)代表的字符串
我們可以在原始的 3 * 8 數(shù)據(jù)之后再添加一個(gè) 隨機(jī)的 8 位數(shù)據(jù),亦或是按照一定規(guī)律打亂 3 個(gè)字節(jié)的順序
在解碼的時(shí)候首先按照正常的Base64解碼得到字節(jié)數(shù)據(jù),然后
按照編碼規(guī)律反向再次解碼得到正確的字節(jié)碼
也許有人會(huì)說(shuō),同行老哥可以通過(guò)
直接查看js文件來(lái)
我們可以通過(guò)
webpack
等工具,在
production環(huán)境,開(kāi)啟
壓縮代碼,并且
開(kāi)啟刪除注釋,其效果與
代碼混淆類(lèi)似正常人一般是看不懂輸出的代碼的(失去了語(yǔ)義化命名和注釋?zhuān)?br>
做了這么多操作,終于是將同行老哥
堪堪抵擋住了
6. 傅里葉大法
- 安全性:High
- 復(fù)雜度:High
- 性能:Low
次方案需要的
專(zhuān)業(yè)知識(shí)較多,
算法比較復(fù)雜作者現(xiàn)在被“關(guān)禁閉”在老家,故而無(wú)法掏出《信號(hào)與系統(tǒng)》來(lái)復(fù)習(xí)知識(shí),這部分只能
后續(xù)補(bǔ)全具體的實(shí)現(xiàn)以及知識(shí)鏈接我放到了參考資料中,大家可以自行閱讀,此方法的魯棒性以及抗擾性都較高
總結(jié)
First of all低透明度隱藏水印、
傅里葉大法都十分依賴于圖片本身的像素質(zhì)量,當(dāng)時(shí)用
手機(jī)拍攝圖片之后,將會(huì)引入大量的噪聲,方法將會(huì)失效
故而對(duì)于較為
重要的文件還是需要
加大明顯水印密度來(lái)制裁手機(jī)拍攝的影響,因?yàn)橹匾募话?b>無(wú)需在意文件完整性(舉個(gè)栗子:機(jī)密圖片上全是水印,但只需要能看清圖片內(nèi)容即可)
Then并不是最復(fù)雜的方法就最好,就比如傅里葉大法,其水印處理時(shí)間可能是上述方案中最長(zhǎng)的
但對(duì)于一些對(duì)于
安全度要求不是那么高的情況,就比如本站用戶文章中的圖片資源,完全沒(méi)有必要
此處所寫(xiě)的方案都是從一個(gè)
前端工程師的角度來(lái)看
如果是一個(gè)后端工程師,那么很多操作是
完全不需要的
但是一個(gè)方案是在前端執(zhí)行還是后端執(zhí)行,總結(jié)起來(lái)就是
it depends根據(jù)自己的需求,在性能和安全度之間做一個(gè)
權(quán)衡參考資料
- 顯性水印+隱藏水印方案
- 數(shù)字水印
- Base64編碼原理
- Base64實(shí)現(xiàn)例子
- 傅里葉轉(zhuǎn)換數(shù)字水印
- 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;}