【導讀】:在月餅事件中,阿里開除了 5 位用指令碼秒殺月餅的技術人員。後有員工對外洩露了內網通報的截圖,洩露者已被查出並處理。後續有媒體報導提到,阿里對員工的訪問介面加了一層肉眼無法識別的東西。有網友對這個技術好奇,在知乎提問:
在月餅事件中的新聞中提到。阿里對員工訪問的介面做了一定的處理。貌似這不是簡單的水印。這種處理是什麼,是怎麼做到的呢?
伯小樂整理了 2 位知乎網友的回答分享。(均已獲授權)
一、liu fuqiang 的分享,3800+ 頂
本文通過一個的實驗,簡要介紹頻域手段新增數字盲水印的方法,並進一步驗證其抗攻擊性。在上述實驗的基礎上,總結躲避數字盲水印的方法。(多圖預警)
本文分為五個部分,第一部分綜述;第二部分頻域數字盲水印製作原理介紹;第三部分盲水印攻擊性實驗;第四部分總結;第五部分附錄(原始碼)。
一、綜述
本文提供的一種實現“阿里通過肉眼無法識別的標識碼追蹤員工”的技術手段。通過看其他答主的分析,阿里可能還沒用到頻域加水印的技術。
在原答案中,存在措辭欠妥之處,對此表示由衷的歉意。
相對於空域方法,頻域加盲水印的方法隱匿性更強,抵抗攻擊能力更強。這類演算法解水印困難,你不知道水印加在那個頻段,而且受到攻擊往往會破壞影象原本內容。本文簡要科普通過頻域手段新增數字盲水印。對於web,可以新增一個背景圖片,來追蹤截圖者。
所謂盲水印,是指人感知不到的水印,包括看不到或聽不見(沒錯,數字盲水印也能夠用於音訊)。其主要應用於音像作品、數字圖書等,目的是,在不破壞原始作品的情況下,實現版權的防護與追蹤。
新增數字盲水印的方法簡單可分為空域方法和頻域方法,這兩種方法新增了冗餘資訊,但在編碼和壓縮情況不變的情況下,不會使原始影象大小產生變化(原來是10MB新增盲水印之後還是10MB)。
空域是指空間域,我們日常所見的影象就是空域。空域新增數字水印的方法是在空間域直接對影象操作(之所以說的這麼繞,是因為不僅僅原圖是空域,原圖的差分等等也是空域),比如將水印直接疊加在影象上。
我們常說一個音有多高,這個音高是指頻率;同樣,影象灰度變化強烈的情況,也可以視為影象的頻率。頻域新增數字水印的方法,是指通過某種變換手段(傅立葉變換,離散餘弦變換,小波變換等)將影象變換到頻域(小波域),在頻域對影象新增水印,再通過逆變換,將影象轉換為空間域。相對於空域手段,頻域手段隱匿性更強,抗攻擊性更高。
所謂對水印的攻擊,是指破壞水印,包括塗抹,剪下,放縮,旋轉,壓縮,加噪,濾波等。數字盲水印不僅僅要敏捷性高(不被人抓到),也要防禦性強(抗打)。就像Dota的敏捷英雄往往是脆皮,數字盲水印的隱匿性和魯棒性是互斥的。(魯棒性是抗攻擊性的學術名字)
二、頻域製作數字盲水印的方法
訊號是有頻率的,一個訊號可以看做是無數個不同階的正弦訊號的的疊加。
上式為傅立葉變換公式,ƒ(t) 是指時域訊號(對於訊號我們說時域,因為是與時間有關的,而影象我們往往說空域,與空間有關),ω 是指頻率。想要對傅立葉變換有深入瞭解的同學,建議看一下《訊號與系統》或者《數字訊號處理》的教材,裡面系統介紹了傅立葉變換、快速傅立葉變換、拉普拉斯變換、z變換等。
簡而言之,我們有方法將時域訊號轉換成為頻域,同樣,我們也能將二維訊號(影象)轉換為頻域。在上文中提到,影象的頻率是指影象灰度變換的強烈情況。關於此方面更系統的知識,參見岡薩雷斯的《影象處理》。
下面以傅立葉變換為例,介紹通過頻域給影象新增數字盲水印的方法。注意,因為影象是離散訊號,我們實際用的是離散傅立葉變換,在本文采用的都是二維快速傅立葉變換,快速傅立葉變換與離散時間傅立葉變換等價,通過蝶型歸併的手段,速度更快。下文中傅立葉變換均為二維快速傅立葉變換。
上圖為疊加數字盲水印的基本流程。編碼的目的有二,一是對水印加密,二控制水印能量的分佈。以下是疊加數字盲水印的實驗。這是原影象,尺寸300*240 (不要問我為什麼不用Lena,那是我前女友),
之後進行傅立葉變換,下圖變換後的頻域影象,
這是我想加的水印,尺寸200*100,
這是我編碼後的水印,編碼方式採用隨機序列編碼,通過編碼,水印分佈到隨機分佈到各個頻率,並且對水印進行了加密,
將上圖與原圖的頻譜疊加,可見影象的頻譜已經發生了巨大的變化,
之後,將疊加水印的頻譜進行傅立葉逆變換,得到疊加數字水印後的影象,
肉眼幾乎看不出疊加水印後的影象與原圖的差異,這樣,數字盲水印已經疊加到影象中去。實際上,我們是把水印以噪聲的形式新增到原影象中。下圖是在空域上的加水印圖與原圖的殘差(調整了對比度,不然殘差調小看不見),
可以看出,實際上上述方法是通過頻域新增冗餘資訊(像噪聲一樣)。這些噪聲遍佈全圖,在空域上並不容易破壞。
最終,均方誤差(MSE)為0.0244
訊雜比(PSNR)為64.2dB那麼,為什麼頻譜發生了巨大的變化,而在空域卻變化如此小呢?這是因為我們避開了影象的主要頻率。下圖是原圖頻譜豎過來的樣子,其能量主要集中在低頻。
水印提取是水印疊加的逆過程,
經提取後,我們得到如下水印,問:為什麼水印要對稱呢?嘿嘿,大家想想看。
三、攻擊性實驗
本部分進行攻擊性實驗,來驗證通過頻域手段疊加數字盲水印的魯棒性。
1.進行塗抹攻擊,這是攻擊後的圖片:
再進行水印提取:
2.進行剪下攻擊,就是網上經常用的截圖擷取一部分的情況:
進行迴圈補全:
提取水印:
3.伸縮攻擊(這個實驗明碼做的,水印能量較高,隱匿性不強):
提取水印(水印加的不好,混頻挺嚴重的):
4.旋轉攻擊(明碼):
提取水印:
5.JPEG壓縮後(這個實驗我好像是拿明碼做的,能量主要加在了高頻):
提取結果:
6.PS 4畫素馬賽克/均值濾波等,攻擊後影象(這是我女朋友嗎?醜死了):
提取水印後影象:
7.截圖,
截圖後我手動摳出要測試的影象區域,並且抽樣或者插值到原圖尺寸:
測試結果:
8. 亮度調節(明碼):
水印提取:
9.色相調節(明碼):
水印提取:
10.飽和度調節(明碼):
水印:
11.對比度(明碼):
水印:
12.評論區用waifu2x去噪後圖片:
解水印:
13.美圖秀秀,我對我女票一鍵美顏,美白,磨皮,加腮紅,加脣彩(有一種很羞恥的感覺,捂臉):
提取水印:
14.對於背景純色的圖其實也是無所謂的
能量係數為10時加水印圖片:覺得太顯噪就把能量係數調低,不過水印的隱祕性和魯棒性是互斥的
最終提取出的水印:
15.我用將RGB>600的畫素設定成為(0,255,0)來模擬PS魔術手,
提取水印為:
16.屏攝,好吧,這個實驗我做哭了
屏攝圖:
實驗結果:
我把水印能量係數調整到2000都沒有用。
屏攝之後與原圖訊雜比為4dB左右,我用多抽樣濾波的方式試過,濾不掉屏攝引入的噪聲。屏攝不僅引入了椒鹽噪聲,乘性噪聲,還有有規律的雪花紋理(摩爾紋)。
四、總結
基於頻域的盲水印方法隱藏性強,魯棒性高,能夠抵禦大部分攻擊。但是,對於盲水印演算法,健壯性和隱匿性是互斥的。
本文方法針對屏攝不行,我多次實驗沒有成功,哪位大神可以做一下或者討論討論。還有二值化不行,這是我想當然的,覺得肯定不行所以沒做實驗。其他的我試了試,用給出的方法調整一下能量係數都可以。我想大家最關心的是什麼最安全,不會被追蹤。
不涉及影象的都安全,比如拿筆記下來。涉及影象的屏攝最安全,截圖十分不安全。
=====彩蛋====
我在上圖明碼寫入了資訊。為了抵抗jpg壓縮,我水印能量較高,並且因為沒有編碼,能量分佈不均。圖中規律性紋路,就是你懂的。嘿嘿,你懂的,解開看看吧。
@楊一丁 在答案中給出了上圖隱寫的內容,(霧)。
五、附錄
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
%% 傅立葉變換加水印原始碼 %% 執行環境 Matlab2010a clc;clear;close all; alpha = 1; %% read data im = double(imread('gl1.jpg'))/255; mark = double(imread('watermark.jpg'))/255; figure, imshow(im),title('original image'); figure, imshow(mark),title('watermark'); %% encode mark imsize = size(im); %random TH=zeros(imsize(1)*0.5,imsize(2),imsize(3)); TH1 = TH; TH1(1:size(mark,1),1:size(mark,2),:) = mark; M=randperm(0.5*imsize(1)); N=randperm(imsize(2)); save('encode.mat','M','N'); for i=1:imsize(1)*0.5 for j=1:imsize(2) TH(i,j,:)=TH1(M(i),N(j),:); end end % symmetric mark_ = zeros(imsize(1),imsize(2),imsize(3)); mark_(1:imsize(1)*0.5,1:imsize(2),:)=TH; for i=1:imsize(1)*0.5 for j=1:imsize(2) mark_(imsize(1)+1-i,imsize(2)+1-j,:)=TH(i,j,:); end end figure,imshow(mark_),title('encoded watermark'); %imwrite(mark_,'encoded watermark.jpg'); %% add watermark FA=fft2(im); figure,imshow(FA);title('spectrum of original image'); FB=FA+alpha*double(mark_); figure,imshow(FB); title('spectrum of watermarked image'); FAO=ifft2(FB); figure,imshow(FAO); title('watermarked image'); %imwrite(uint8(FAO),'watermarked image.jpg'); RI = FAO-double(im); figure,imshow(uint8(RI)); title('residual'); %imwrite(uint8(RI),'residual.jpg'); xl = 1:imsize(2); yl = 1:imsize(1); [xx,yy] = meshgrid(xl,yl); figure, plot3(xx,yy,FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of original image'); figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2),title('spectrum of watermarked image'); figure, plot3(xx,yy,FB(:,:,1).^2+FB(:,:,2).^2+FB(:,:,3).^2-FA(:,:,1).^2+FA(:,:,2).^2+FA(:,:,3).^2),title('spectrum of watermark'); %% extract watermark FA2=fft2(FAO); G=(FA2-FA)/alpha; GG=G; for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(M(i),N(j),:)=G(i,j,:); end end for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:); end end figure,imshow(GG);title('extracted watermark'); %imwrite(uint8(GG),'extracted watermark.jpg'); %% MSE and PSNR C=double(im); RC=double(FAO); MSE=0; PSNR=0; for i=1:imsize(1) for j=1:imsize(2) MSE=MSE+(C(i,j)-RC(i,j)).^2; end end MSE=MSE/360.^2; PSNR=20*log10(255/sqrt(MSE)); MSE PSNR %% attack test %% attack by smearing %A = double(imread('gl1.jpg')); %B = double(imread('attacked image.jpg')); attack = 1-double(imread('attack.jpg'))/255; figure,imshow(attack); FAO_ = FAO; for i=1:imsize(1) for j=1:imsize(2) if attack(i,j,1)+attack(i,j,2)+attack(i,j,3)>0.5 FAO_(i,j,:) = attack(i,j,:); end end end figure,imshow(FAO_); %extract watermark FA2=fft2(FAO_); G=(FA2-FA)*2; GG=G; for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(M(i),N(j),:)=G(i,j,:); end end for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:); end end figure,imshow(GG);title('extracted watermark'); %% attack by cutting s2 = 0.8; FAO_ = FAO; FAO_(:,s2*imsize(2)+1:imsize(2),:) = FAO_(:,1:int32((1-s2)*imsize(2)),:); figure,imshow(FAO_); %extract watermark FA2=fft2(FAO_); G=(FA2-FA)*2; GG=G; for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(M(i),N(j),:)=G(i,j,:); end end for i=1:imsize(1)*0.5 for j=1:imsize(2) GG(imsize(1)+1-i,imsize(2)+1-j,:)=GG(i,j,:); end end figure,imshow(GG);title('extracted watermark'); %%小波變換加水印,解水印大家按照加的思路逆過來就好 clc;clear;close all; %% read data im = double(imread('gl1.jpg'))/255; mark = double(imread('watermark.jpg'))/255; figure, imshow(im),title('original image'); figure, imshow(mark),title('watermark'); %% RGB division im=double(im); mark=double(mark); imr=im(:,:,1); markr=mark(:,:,1); img=im(:,:,2); markg=mark(:,:,2); imb=im(:,:,3); markb=mark(:,:,3); %% parameter r=0.04; g = 0.04; b = 0.04; %% wavelet tranform and add watermark % for red [Cwr,Swr]=wavedec2(markr,1,'haar'); [Cr,Sr]=wavedec2(imr,2,'haar'); % add watermark Cr(1:size(Cwr,2)/16)=... Cr(1:size(Cwr,2)/16)+r*Cwr(1:size(Cwr,2)/16); k=0; while k<=size(Cr,2)/size(Cwr,2)-1 Cr(1+size(Cr,2)/4+k*size(Cwr,2)/4:size(Cr,2)/4+... (k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/4+... k*size(Cwr,2)/4:size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+... r*Cwr(1+size(Cwr,2)/4:size(Cwr,2)/2); Cr(1+size(Cr,2)/2+k*size(Cwr,2)/4:size(Cr,2)/2+... (k+1)*size(Cwr,2)/4)=Cr(1+size(Cr,2)/2+... k*size(Cwr,2)/4:size(Cr,2)/2+(k+1)*size(Cwr,2)/4)+... r*Cwr(1+size(Cwr,2)/2:3*size(Cwr,2)/4); Cr(1+3*size(Cwr,2)/4+k*size(Cwr,2)/4:3*size(Cwr,2)/4+... (k+1)*size(Cwr,2)/4)=Cr(1+3*size(Cr,2)/4+... k*size(Cwr,2)/4:3*size(Cr,2)/4+(k+1)*size(Cwr,2)/4)+... r*Cwr(1+3*size(Cwr,2)/4:size(Cwr,2)); k=k+1; end; Cr(1:size(Cwr,2)/4)=Cr(1:size(Cwr,2)/4)+r*Cwr(1:size(Cwr,2)/4); % for green [Cwg,Swg]=WAVEDEC2(markg,1,'haar'); [Cg,Sg]=WAVEDEC2(img,2,'haar'); Cg(1:size(Cwg,2)/16)=... Cg(1:size(Cwg,2)/16)+g*Cwg(1:size(Cwg,2)/16); k=0; while k<=size(Cg,2)/size(Cwg,2)-1 Cg(1+size(Cg,2)/4+k*size(Cwg,2)/4:size(Cg,2)/4+... (k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/4+... k*size(Cwg,2)/4:size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+... g*Cwg(1+size(Cwg,2)/4:size(Cwg,2)/2); Cg(1+size(Cg,2)/2+k*size(Cwg,2)/4:size(Cg,2)/2+... (k+1)*size(Cwg,2)/4)=Cg(1+size(Cg,2)/2+... k*size(Cwg,2)/4:size(Cg,2)/2+(k+1)*size(Cwg,2)/4)+... g*Cwg(1+size(Cwg,2)/2:3*size(Cwg,2)/4); Cg(1+3*size(Cg,2)/4+k*size(Cwg,2)/4:3*size(Cg,2)/4+... (k+1)*size(Cwg,2)/4)=Cg(1+3*size(Cg,2)/4+... k*size(Cwg,2)/4:3*size(Cg,2)/4+(k+1)*size(Cwg,2)/4)+... g*Cwg(1+3*size(Cwg,2)/4:size(Cwg,2)); k=k+1; end; Cg(1:size(Cwg,2)/4)=Cg(1:size(Cwg,2)/4)+g*Cwg(1:size(Cwg,2)/4); % for blue [Cwb,Swb]=WAVEDEC2(markb,1,'haar'); [Cb,Sb]=WAVEDEC2(imb,2,'haar'); Cb(1:size(Cwb,2)/16)+b*Cwb(1:size(Cwb,2)/16); k=0; while k<=size(Cb,2)/size(Cwb,2)-1 Cb(1+size(Cb,2)/4+k*size(Cwb,2)/4:size(Cb,2)/4+... (k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/4+... k*size(Cwb,2)/4:size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+... g*Cwb(1+size(Cwb,2)/4:size(Cwb,2)/2); Cb(1+size(Cb,2)/2+k*size(Cwb,2)/4:size(Cb,2)/2+... (k+1)*size(Cwb,2)/4)=Cb(1+size(Cb,2)/2+... k*size(Cwb,2)/4:size(Cb,2)/2+(k+1)*size(Cwb,2)/4)+... b*Cwb(1+size(Cwb,2)/2:3*size(Cwb,2)/4); Cb(1+3*size(Cb,2)/4+k*size(Cwb,2)/4:3*size(Cb,2)/4+... (k+1)*size(Cwb,2)/4)=Cb(1+3*size(Cb,2)/4+... k*size(Cwb,2)/4:3*size(Cb,2)/4+(k+1)*size(Cwb,2)/4)+... b*Cwb(1+3*size(Cwb,2)/4:size(Cwb,2)); k=k+1; end; Cb(1:size(Cwb,2)/4)=Cb(1:size(Cwb,2)/4)+b*Cwb(1:size(Cwb,2)/4); %% image reconstruction imr=WAVEREC2(Cr,Sr,'haar'); img=WAVEREC2(Cg,Sg,'haar'); imb=WAVEREC2(Cb,Sb,'haar'); imsize=size(imr); FAO=zeros(imsize(1),imsize(2),3); for i=1:imsize(1); for j=1:imsize(2); FAO(i,j,1)=imr(i,j); FAO(i,j,2)=img(i,j); FAO(i,j,3)=imb(i,j); end end figure, imshow(FAO); title('watermarked image'); |
二、shotgun 的回答分享,254 頂
其實如果防禦做得好,想繞過還真的沒那麼容易。按照DLP,也就是資料防洩密的思路:
1.重要的圖片或者文件內加水印,檔案裡面再加上全息數字水印(這個大家都說得很全面了)
然而這就足夠了嗎?或者說只有這些防護?乃義務!
2.所有終端監控敏感操作:另存、截圖、剪貼簿、對敏感文件的操作等等,上述行為一律上傳到審計伺服器上備份待查。
3.禁止未授權(未安裝終端防護程式)的終端接入內網和業務系統。
4.有敏感操作或者跨終端登入的時候通過攝像頭拍攝人臉(知道為什麼祖克伯要貼攝像頭了吧)
5.定期對硬碟進行檔案掃描,關鍵字、相似度、OCR等方法識別出可能是敏感或者涉密的檔案,並提取特徵,進行標定。
6.同樣採用雜湊值、關鍵字、相似度匹配等方法檢查所有網路出口:郵件、聊天、網站、網盤等,對傳輸內容全部解碼進行審計並且記錄。
7.檢查或者關閉所有物理出口,USB、Wi-Fi、藍芽、音訊口(防止通過耳機口盜竊資料)。
8.所有檔案落地加密(檔案加密或者全盤加密),偷硬碟也沒用。
9.所有電子裝置進出辦公區域全部稽核檢查。
10.核心機密文件不落地,存放於核心伺服器,採用遠端終端或者虛擬視窗方式查閱/編輯。
別笑,富士康和華為基本上就是這麼幹的。