驗證碼這種東西真的是反人類。雖然它在保證賬號安全、反作弊以及反廣告有著至關重要的作用,但對於普通使用者來說,輸驗證碼很多時候實在是讓人抓狂。
文摘菌18歲的時候幫朋友刷QQ空間留言就天天和驗證碼作鬥爭,前幾天傳一個影片又創下了連續7次輸錯驗證碼的記錄。不過好在文摘菌最近發現,用機器學習破解簡單驗證碼已經是妥妥的小事了。
今天,文摘菌就帶來了一個15分鐘黑掉世界上最受歡迎的驗證碼外掛的小教程。歡迎開啟新年第一黑。
先給大家介紹一下今天我們要黑的驗證碼外掛。在Wordpress官網的外掛註冊頁面(https://wordpress.org/plugins/)搜尋“captcha”,返回的第一條結果“Really Simple CAPTCHA”就是今天我們要開刀的外掛了。
由於這個外掛是開源的,我們可以用它的原始碼任意生成我們訓練需要的驗證碼圖片了。為了讓這件事更有一點挑戰性,我們不如給自己設定一個問題解決的時間限制——就15分鐘吧!
插播:我絕對沒有任何批評“Really Simple CAPTCHA”這個外掛或它的作者的意思。這個外掛的作者本人也承認這個外掛已難以保證安全性了,並建議大家使用別的方法。本文只是在單純地闡述一個有趣而快捷的技能挑戰。然而,如果你正好是這個外掛的100萬多個使用者裡的一個,可能你是時候換個別的工具了。
熱身準備
為了制定一個作戰計劃,讓我們先來看看“Really Simple CAPTCHA”這個外掛能夠生成的是什麼型別的圖片吧。
好吧,看來這是個由4個字母組成的驗證圖片。讓我們核實一下它的PHP原始碼:
沒錯,它用了任意混合4種不同的字型的方式來生成了4個字元的驗證碼。我們可以看到,這個系統為了避免使用者混淆字母和數字,在程式碼中設定了從來不使用O和I這兩個字母。所以算下來我們需要識別的字母和數字共有32個。
目前用時:2分鐘。
工具一覽
工欲善其事,必先利其器。要解決我們的問題,我們需要用到以下工具:
Python 3
Python有很多機器學習和計算機視覺庫可以呼叫。
OpenCV
OpenCV是一個目前流行的用於計算機視覺和影像處理的框架,我們需要用到它去處理CAPTCHA驗證碼影像。這個框架擁有Python API,因此我們可以直接使用Python呼叫它。
Keras
Keras是一個用Python編寫的深度學習框架,它使用極少的程式碼就可以簡單地實現對深度神經網路的定義、訓練和應用。
TensorFlow
TensorFlow是谷歌的機器學習庫。雖然我們將會在Keras中編碼,但Keras自己實際上並不會執行神經網路的邏輯,而是背地裡把所有的髒活累活都丟給谷歌的TensorFlow機器學習庫去處理。
好了,說完工具,讓我們回到挑戰本身吧。
建立資料集
為了訓練機器學習系統,我們首先需要訓練資料。而為了破解CAPTCHA系統,我們需要的訓練資料應該長這樣:
由於我們已經有了WordPress外掛的原始碼了,因此我們只需要對其原始碼小作改動,就可以得到10,000張驗證碼圖片及其相對應的答案。
在花費了數分鐘來搗騰程式碼並增加了一個簡單的“for”迴圈之後,我得到了一個裝滿了訓練資料的資料夾,裡面有10,000個PNG格式的檔案,檔名就是與之匹配的正確答案:
這是全文唯一一個我不會給你們示範程式碼的部分。我們在做的事情是出於學習和教育目的,並非真的要你們在現實中去黑掉WordPress的網站。不過,我將會給你們我在最後生成的那10,000張圖片,以便你們可以複製我的結果。
目前用時:5分鐘
簡化問題
現在已經有了可用的訓練資料,我們可以直接用這些資料去訓練一個神經網路:
在訓練資料足夠多的情況下,這個方法應該是可行的,但我們還能把問題進一步簡化。在僅有15分鐘的情況下,問題越簡單,我們所需要的訓練資料和計算能力也就越少。
幸運的是這些CAPTCHA圖片始終只有4個字母組成,如果我們能夠把圖片分割開讓每個字母成為一張獨立的圖片,那麼我們就可以訓練神經網路讓它逐個識別字母。
手動用PS分割圖片顯然是不現實的——我們現在只剩下10分鐘了。同時,我們也無法把那些影像進行四等分的切割,因為CAPTCHA系統為了防止如下情況(如左側動圖),會隨機地把字元放置在不同水平高度的位置上。
字元在每張圖片中是隨機放置的,使得分開這些字元變得略為困難
幸運的是,我們仍可以自動化實現這個過程。在影像處理的過程中,我們通常需要探測出那些顏色相同的畫素“斑點”,而環繞這些連續的畫素斑點的邊界則被稱為“輪廓線”。OpenCV恰好有一個自帶的叫做findContours()的函式,可以用於檢測那些連續的區域。
那麼,我們首先從一張未經處理的CAPTCHA圖片開始:
然後為了方便我們找到那些連續的區域,我們要將這種圖片轉化成純粹的黑白影像(這個過程被稱作二值化):
接下來,我們將要使用OpenCV的findContours()函式去檢測出那些包含了連續且顏色相同的畫素斑點的部分:
之後我們需要做的事情很簡單,只要把每個區域作為獨立的影像檔案儲存下來就好了。同時,因為我們已知每張圖片包含了從左到右排列的四個字母,在儲存每個字母影像的時候我們可以按照排列的順序來進行標記。只要我們儲存圖片的順序是無誤的,那麼我們就能夠保證用正確的字母來標註每個圖片。
但在這時,我突然發現了一個問題!這些驗證碼圖片的字母有的時候是重疊在一起的:
這意味著某些提取出來的影像,在一個獨立的區域裡實際上混合了兩個字母:
如果我們不及時解決這個問題,那麼我們生產出來的將是一堆劣質的訓練資料。為了防止機器誤以為這種由兩個字母擠成一團的影像當成是一個字母,我們必須要修正這個問題。
這裡有一個簡單的小技巧:如果一個單獨等高線內的區域的寬度遠遠大於它的高度,那麼我們可以推測這個區域內可能有兩個字母擠壓在一起了。在這種情況下,我們可以直接把合併的字母對半切割並當作兩個獨立分開的字母:
我們將會對半分開任何寬度遠大於高度的區域,並將其按兩個獨立的字母來處理。這個技巧聽起來有點不靠譜,但是應用在這些CAPTHCA驗證碼圖片上的效果卻很不錯。
現在我們已經有了提取單獨字母的方法了,接下來可以用來處理我們手頭上所有的CAPTCHA驗證碼圖片了。我們的目標是收集每個字母的不同變體,並且把這些變體統一整理歸類在其所屬字母的資料夾裡。
下面這張圖展示的就是我在對所有圖片進行字母提取之後裝著所有“W”的資料夾:
其中有些“W”字母是從那10,000 CAPCHA圖片中提取出來的,我最後得到了1,147個不同的“W”影像。
目前用時:10分鐘
訓練神經網路
由於我們只需要辨識單個字母和數字的圖片,我們不需要用到非常複雜的神經網路結構,畢竟辨識字元要比辨識一些如小貓小狗之類的較複雜的圖片簡單得多了。
我們將要使用的是一個結構簡單的卷積神經網路,裡面有兩個卷積層和兩個完全連線的隱藏層和輸出層:
如果大家想知道更多關於卷積神經網路如何運作,以及為什麼它們是影像識別的理想方法,可以去看看這篇文章
(https://medium.com/@ageitgey/machine-learning-is-fun-part-3-deep-learning-and-convolutional-neural-networks-f40359318721)或者這本書
(https://www.pyimagesearch.com/deep-learning-computer-vision-python-book/)。定義這個神經網路結構只需要利用Keras來寫上幾行程式碼:
現在,我們可以進行訓練了!
在對訓練資料集進行了10個迴圈的訓練之後,我們得到了幾乎100%的準確率。這時候,我們應該就能夠隨時隨地自動繞過這個CAPTCHA系統了!
目前用時:15分鐘(哈!)
牛刀小試
好了,現在我們有一個已經訓練好的神經網路模型了,接下來破解一個真正的CAPTCHA系統就相當簡單了:
從一個網站上抓取一個使用WordPress外掛的真實CAPTCHA影像。
利用我們剛剛建立訓練資料集的方法,把一張CAPTCHA驗證碼圖片分成四張獨立的字元圖片。
讓我們的神經網路對每個字母圖片進行預測。
將模型預測出的4個字元作為驗證問題的答案。
新年第一黑完美收工!
我們最後得到的驗證碼破解系統長這樣:
也可以用終端實現破解:
動手來試試吧!
這篇教程每個步驟設計的程式碼都儲存在了這兒:
https://s3-us-west-2.amazonaws.com/mlif-example-code/solving_captchas_code_examples.zip
壓縮包中的REAME檔案說明了這些程式碼該如何執行。裡面也包含了10,000訓練樣本圖片。
簡直不敢信,全球最流行的驗證碼就這麼被我們黑掉了。不過也沒啥好高興的,畢竟面對下面這種驗證外掛,現在的AI是一點脾氣也沒有的。
原文連結:
https://medium.com/@ageitgey/how-to-break-a-captcha-system-in-15-minutes-with-machine-learning-dbebb035a710