驗證碼這種東西真的是反人類。雖然它在保證賬号安全、反作弊以及反廣告有着至關重要的作用,但對于普通使用者來說,輸驗證碼很多時候實在是讓人抓狂。
文摘菌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是一點脾氣也沒有的。
原文釋出時間為:2018-01-02
本文作者:文摘菌