防爬蟲做過的那些事:為什麼後來全部拿掉

Mork 上面的東西是使用者傳的,而短連結本身沒有門檻,拿到網址就打得開,早期我們很在意這件事:如果有人寫個程式把整站的短碼掃過一遍,等於把別人的照片跟影片整批帶走。

所以那幾年陸陸續續做了不少防抓取的機制,從 2020 年前後開始,前後大概三年,一層一層疊上去。

經營網站,團隊也有過一些爬蟲經驗,了解市面上的防爬方案多半是擋在前面的 WAF 規則,而 WAF 繞過只是時間的問題,能處理的是請求的頻率跟來源過濾,團隊想擋的是「內容被完整帶走」,盡可能讓使用者的隱私安全不受影響,那得從輸出本身下手,所以每一層都是自己寫的影像處理、隱寫、前端解密、指紋比對,一路做到自動封鎖。

不要讓圖片是一張圖片

最早的想法很直接:只要輸出的不是 <img src="...">,一般的抓取程式就拿不到。

於是圖片改成內嵌的 base64 字串,塞在頁面裡送出去,後來又加了一層,把 base64 再加密一次,放在 alt 屬性裡,真正的 src 指向多張梗圖中的其中一張,等前端的腳本解完密才把畫面換掉,甚至連前端 JS 都混淆處理,有效的讓有心人無法得逞。

同一份程式裡還準備了三種輸出模式,每次請求隨機挑一種,有的走 <img>,有的包在 <iframe> 裡,有的整段交給 JavaScript 產生,連類別名稱、函式名稱、canvas 的 id 都是每次隨機生成的亂碼,所以兩次載入同一個頁面,原始碼長得完全不一樣,不斷嘗試拉高爬蟲者成本。

圖片切片

另一條路是把圖切成三條橫向的長條,分別輸出,靠 CSS 疊回原本的樣子,看起來是一張圖,存下來卻只有三分之一,中間還會穿插隱形的梗圖。

再往上是雜訊。輸出之前在圖上隨機畫幾十個點、幾條線跟幾個弧,肉眼幾乎看不出來,但每一次輸出的位元組都不一樣,做雜湊比對的抓取程式會認為每次拿到的都是新檔案,無法透過 Hash 來比對是不是爬到梗圖。

另外還準備了一批假圖,判定為抓取程式的請求不會被擋掉,而是照樣拿到一個看起來正常的頁面,只是裡面的圖被換掉了,回顧當前寫的程式碼,註解寫得很直白:用於可以給特定人士假資料。

看不見的浮水印

這是做得最深的一層。

輸出 PNG 的時候,會先把「來源 IP 加上當下時間」這串字加密,編成一個 QR Code,然後把那個 QR Code 寫進圖片右上角像素的最低位元裡。最低位元的變化在視覺上是看不出來的,圖看起來跟原本一模一樣。

用意是萬一有人把內容轉貼到別的地方,從那張圖就能反推出是誰在什麼時候取走的。

這一層花的時間最久。要讓 QR Code 在只動最低位元的情況下還原得回來,得處理透明通道、對齊、以及重新編碼會不會把那一位元洗掉——所以只在 PNG 上做,JPEG 的失真壓縮會把資訊直接吃掉。這種細節沒有現成的東西可以抄,只能一次一次試。

瀏覽器特徵與自動封鎖

再來是認人,解鎖密碼保護的連結之後,那一次的輸出會夾帶一段腳本,收集 canvas 繪圖的雜湊、WebGL 的雜湊,以及一個訪客識別碼,回報給伺服器。

伺服器拿到之後跟一組已知的特徵比對,命中就把那個 IP 送進 CDN 的封鎖清單,同時寫一筆警示紀錄,整個過程不需要人介入。

現在回頭看,這一層的問題最大,比對的條件是一個寫死的 WebGL 雜湊值只要有人的顯示卡跟驅動組合剛好算出同一個雜湊就會被封,而且是在 CDN 層被封,連錯誤訊息都看不到,也確實在實務上誤擋了許多人。

家用 IP 是會換的

封 IP 本身就是最沒效率,且風險極高的行為。

台灣多數的家用寬頻是動態位址,重開機就換一個,今天封掉的那個 IP,明天可能輪到隔壁棟的另一個人,而被封的那個人只要重開數據機就沒事了,也就是說,這個機制對真正想抓的人幾乎沒有效果,對誤判的一般使用者卻是全損。

而認真的抓取者本來就不會用自己家的線路。市面上的住宅代理服務就是一大批真實的家用 IP 在輪替,行為上跟一般訪客沒有兩樣,要靠 IP 分辨誰是誰,前提是一個位址長期對應同一個人,那個前提在這裡不成立,但還是有透過人工分析找到爬蟲機器人,短時間內進行爬蟲的阻斷。

回歸保護本質

市場觀察多年,發現多數人分享都是建立在信任上,不認為查看者會私下保存或是二次利用,於是根本不在乎保護,而是將分享擺在第一位,這是一類客群,大多是將影像投放到社群媒體平台,以半公開的方式讓網友閱覽,而另一種是單純的私下分享,這種也是團隊比較想保護的群體,在這情況的分享下,信任通常尚未完全建立。

在過去保護機制下,每一次瀏覽都要重新編碼一張圖,而 base64 讓內容夾在 HTML 裡,CDN 沒辦法快取,體積還比原本多三分之一,不僅用戶不在乎,效能下降還遭受抱怨。

最後是想清楚了要防的到底是什麼,內容本來就是要給人看的,對方手上有短網址,就代表建立連結的人願意給他看,真正該防的不是「看」,而是「整站被自動化掃走」,而那件事用瀏覽次數上限、有效期限跟速率限制就處理掉了,現在甚至有動態 Turnstile 機制,那些機制已經是安全與用戶體驗間的取捨了。

留下來的部分

使用者不在乎,不代表我們不需要做,能保護每一個用戶仍然是 Mork 的使用,團隊依然定期執行滲透測試檢測、弱點掃描,確認平台安全性,以及兼顧那些重要且不被看重的細節:

  • GPS 座標與 EXIF 是在瀏覽器裡清掉的,清完才上傳,伺服器從頭到尾沒有見過那些座標。多數服務是收到檔案之後在伺服器端處理,那中間有一段時間差,細節寫在另一篇
  • 不需要註冊也不需要留下任何聯絡方式,上傳跟管理都不用帳號。
  • 次數上限是以人計算,同一個人重看不會多扣額度——這件事需要在不建立帳號的前提下分辨「同一個人」,本身就是前面那幾年指紋研究留下來的東西。
  • 期限到了連結就失效,不必回頭手動刪。

把力氣從「不讓別人拿走」換到「不讓使用者被自己上傳的東西出賣」,是這幾年最值得的一次調整,前者做到滿也擋不住有心人,後者做對了,每一個上傳的人都受益。

← 回到技術筆記