驗證碼是目前網際網路上非常常見也是非常重要的一個事物,充當着很多系統的 防火牆 功能,但是随時OCR技術的發展,驗證碼暴露出來的安全問題也越來越嚴峻。本文介紹了一套字元驗證碼識别的完整流程,對于驗證碼安全和OCR識别技術都有一定的借鑒意義。
文章更新:2017-09-20
本文的基于傳統的機器學習SVM的源碼共享:
介紹文章:http://www.cnblogs.com/beer/p/7279136.html
然後經過了一年的時間,筆者又研究和get到了一種更強大的基于CNN卷積神經網絡的直接端到端的驗證識别技術(文章不是我的,然後我把源碼整理了下,介紹和源碼在這裡面):
基于python語言的tensorflow的‘端到端’的字元型驗證碼識别源碼整理(github源碼分享)
文章介紹:http://www.cnblogs.com/beer/p/7392397.html
關鍵詞:安全,字元圖檔,驗證碼識别,OCR,Python,SVM,PIL
本文研究所用素材來自于某舊Web架構的網站 完全對外公開 的公共圖檔資源。
本文隻做了該網站對外公開的公共圖檔資源進行了爬取, 并未越權 做任何多餘操作。
本文在書寫相關報告的時候已經 隐去 漏洞網站的身份資訊。
本文作者 已經通知 網站相關人員此系統漏洞,并積極向新系統轉移。
本報告的主要目的也僅是用于 OCR交流學習 和引起大家對 驗證安全的警覺 。
關于驗證碼的非技術部分的介紹,可以參考以前寫的一篇科普類的文章:
網際網路安全防火牆(1)--網絡驗證碼的科普
http://www.cnblogs.com/beer/p/4996833.html
裡面對驗證碼的種類,使用場景,作用,主要的識别技術等等進行了講解,然而并沒有涉及到任何技術内容。本章内容則作為它的 技術補充 來給出相應的識别的解決方案,讓讀者對驗證碼的功能及安全性問題有更深刻的認識。
要達到本文的目的,隻需要簡單的程式設計知識即可,因為現在的機器學習領域的蓬勃發展,已經有很多封裝好的開源解決方案來進行機器學習。普通程式員已經不需要了解複雜的數學原理,即可以實作對這些工具的應用了。
主要開發環境:
<dl></dl>
<dt>python3.5</dt>
<dd></dd>
python SDK版本
<dt>PIL</dt>
圖檔處理庫
<dt>libsvm</dt>
開源的svm機器學習庫
關于環境的安裝,不是本文的重點,故略去。
一般情況下,對于字元型驗證碼的識别流程如下:
準備原始圖檔素材
圖檔預處理
圖檔字元切割
圖檔尺寸歸一化
圖檔字元标記
字元圖檔特征提取
生成特征和标記對應的訓練資料集
訓練特征标記資料生成識别模型
使用識别模型預測新的未知圖檔集
達到根據“圖檔”就能傳回識别正确的字元集的目标
由于本文是以初級的學習研究目的為主,要求 “有代表性,但又不會太難” ,是以就直接在網上找個比較有代表性的簡單的字元型驗證碼(感覺像在找漏洞一樣)。
最後在一個比較舊的網站(估計是幾十年前的網站架構)找到了這個驗證碼圖檔。
原始圖:
放大清晰圖:
此圖檔能滿足要求,仔細觀察其具有如下特點。
有利識别的特點 :
由純阿拉伯數字組成
字數為4位
字元排列有規律
字型是用的統一字型
以上就是本文所說的此驗證碼簡單的重要原因,後續代碼實作中會用到
不利識别的特點 :
圖檔背景有幹擾噪點
這雖然是不利特點,但是這個幹擾門檻太低,隻需要簡單的方法就可以除去
由于在做訓練的時候,需要大量的素材,是以不可能用手工的方式一張張在浏覽器中儲存,故建議寫個自動化下載下傳的程式。
主要步驟如下:
通過浏覽器的抓包功能擷取随機圖檔驗證碼生成接口
批量請求接口以擷取圖檔
将圖檔儲存到本地磁盤目錄中
這些都是一些IT基本技能,本文就不再詳細展開了。
關于網絡請求和檔案儲存的代碼,如下:
循環執行N次,即可儲存N張驗證素材了。
下面是收集的幾十張素材庫儲存到本地檔案的效果圖:
雖然目前的機器學習算法已經相當先進了,但是為了減少後面訓練時的複雜度,同時增加識别率,很有必要對圖檔進行預處理,使其對機器識别更友好。
針對以上原始素材的處理步驟如下:
讀取原始圖檔素材
将彩色圖檔二值化為黑白圖檔
去除背景噪點
将RGB彩圖轉為灰階圖
将灰階圖按照設定門檻值轉化為二值圖
上面引用到的二值函數的定義如下:
由PIL轉化後變成二值圖檔:0表示黑色,1表示白色。二值化後帶噪點的 6937 的像素點輸出後如下圖:
如果你是近視眼,然後離螢幕遠一點,可以隐約看到 6937 的骨架了。
在轉化為二值圖檔後,就需要清除噪點。本文選擇的素材比較簡單,大部分噪點也是最簡單的那種 孤立點,是以可以通過檢測這些孤立點就能移除大量的噪點。
關于如何去除更複雜的噪點甚至幹擾線和色塊,有比較成熟的算法: 洪水填充法 Flood Fill ,後面有興趣的時間可以繼續研究一下。
本文為了問題簡單化,幹脆就用一種簡單的自己想的 簡單辦法 來解決掉這個問題:
對某個 黑點 周邊的九宮格裡面的黑色點計數
如果黑色點少于2個則證明此點為孤立點,然後得到所有的孤立點
對所有孤立點一次批量移除。
下面将詳細介紹關于具體的算法原理。
将所有的像素點如下圖分成三大類
頂點A
非頂點的邊界點B
内部點C
種類點示意圖如下:
其中:
A類點計算周邊相鄰的3個點(如上圖紅框所示)
B類點計算周邊相鄰的5個點(如上圖紅框所示)
C類點計算周邊相鄰的8個點(如上圖紅框所示)
當然,由于基準點在計算區域的方向不同,A類點和B類點還會有細分:
A類點繼續細分為:左上,左下,右上,右下
B類點繼續細分為:上,下,左,右
C類點不用細分
然後這些細分點将成為後續坐标擷取的準則。
主要算法的python實作如下:
Tips:這個地方是相當考驗人的細心和耐心程度了,這個地方的工作量還是蠻大的,花了半個晚上的時間才完成的。
計算好每個像素點的周邊像素黑點(注意:PIL轉化的圖檔黑點的值為0)個數後,隻需要篩選出個數為 1或者2 的點的坐标即為 孤立點 。這個判斷方法可能不太準确,但是基本上能夠滿足本文的需求了。
經過預處理後的圖檔如下所示:
對比文章開頭的原始圖檔,那些 孤立點 都被移除掉,相對比較 幹淨 的驗證碼圖檔已經生成。
由于字元型 驗證碼圖檔 本質就可以看着是由一系列的 單個字元圖檔 拼接而成,為了簡化研究對象,我們也可以将這些圖檔分解到 原子級 ,即: 隻包含單個字元的圖檔。
于是,我們的研究對象由 “N種字串的組合對象” 變成 “10種阿拉伯數字” 的處理,極大的簡化和減少了處理對象。
現實生活中的字元驗證碼的産生千奇百怪,有各種扭曲和變形。關于字元分割的算法,也沒有很通用的方式。這個算法也是需要開發人員仔細研究所要識别的字元圖檔的特點來制定的。
當然,本文所選的研究對象盡量簡化了這個步驟的難度,下文将慢慢進行介紹。
使用圖像編輯軟體(PhoneShop或者其它)打開驗證碼圖檔,放大到像素級别,觀察其它一些參數特點:
可以得到如下參數:
整個圖檔尺寸是 40*10
單個字元尺寸是 6*10
左右字元和左右邊緣相距2個像素
字元上下緊挨邊緣(即相距0個像素)
這樣就可以很容易就定位到每個字元在整個圖檔中占據的像素區域,然後就可以進行分割了,具體代碼如下:
然後就能得到被切割的 原子級 的圖檔元素了:
基于本部分的内容的讨論,相信大家已經了解到了,如果驗證碼的幹擾(扭曲,噪點,幹擾色塊,幹擾線……)做得不夠強的話,可以得到如下兩個結論:
4位字元和40000位字元的驗證碼差別不大
<dt></dt>
純數字 和 數字及字母組合 的驗證碼差別不大
純數字。分類數為10
<dt>純字母</dt>
不區分大小寫。分類數為26
區分大小寫。分類數為52
數字和區分大小寫的字母組合。分類數為62
在沒有形成 指數級或者幾何級 的難度增加,而隻是 線性有限級 增加計算量時,意義不太大。
本文所選擇的研究對象本身尺寸就是統一狀态:6*10的規格,是以此部分不需要額外處理。但是一些進行了扭曲和縮放的驗證碼,則此部分也會是一個圖像處理的難點。
在前面的環節,已經完成了對單個圖檔的處理和分割了。後面就開始進行 識别模型 的訓練了。
整個訓練過程如下:
大量完成預處理并切割到原子級的圖檔素材準備
對素材圖檔進行人為分類,即:打标簽
定義單張圖檔的識别特征
使用SVM訓練模型對打了标簽的特征檔案進行訓練,得到模型檔案
本文在訓練階段重新下載下傳了同一模式的4數字的驗證圖檔總計:3000張。然後對這3000張圖檔進行處理和切割,得到12000張原子級圖檔。
在這12000張圖檔中删除一些會影響訓練和識别的強幹擾的幹擾素材,切割後的效果圖如下:
由于本文使用的這種識别方法中,機器在最開始是不具備任何 數字的觀念的。是以需要人為的對素材進行辨別,告訴 機器什麼樣的圖檔的内容是 1……。
這個過程叫做 “标記”。
具體打标簽的方法是:
為0~9每個數字建立一個目錄,目錄名稱為相應數字(相當于标簽)
人為判定 圖檔内容,并将圖檔拖到指定數字目錄中
<dt>每個目錄中存放100張左右的素材</dt>
一般情況下,标記的素材越多,那麼訓練出的模型的分辨能力和預測能力越強。例如本文中,标記素材為十多張的時候,對新的測試圖檔識别率基本為零,但是到達100張時,則可以達到近乎100%的識别率
對于切割後的單個字元圖檔,像素級放大圖如下:
從宏觀上看,不同的數字圖檔的本質就是将黑色按照一定規則填充在相應的像素點上,是以這些特征都是最後圍繞像素點進行。
字元圖檔 寬6個像素,高10個像素 ,理論上可以最簡單粗暴地可以定義出60個特征:60個像素點上面的像素值。但是顯然這樣高次元必然會造成過大的計算量,可以适當的降維。
通過查閱相應的文獻 [2],給出另外一種簡單粗暴的特征定義:
每行上黑色像素的個數,可以得到10個特征
每列上黑色像素的個數,可以得到6個特征
最後得到16維的一組特征,實作代碼如下:
然後就将圖檔素材特征化,按照 libSVM 指定的格式生成一組帶特征值和标記值的向量檔案。内容示例如下:
說明如下:
第一列是标簽列,即此圖檔人為标記值,後續還有其它數值1~9的标記
後面是16組特征值,冒号前面是索引号,後面是值
如果有1000張訓練圖檔,那麼會産生1000行的記錄
對此檔案格式有興趣的同學,可以到 libSVM 官網搜尋更多的資料。
到這個階段後,由于本文直接使用的是開源的 libSVM 方案,屬于應用了,是以此處内容就比較簡單的。隻需要輸入特征檔案,然後輸出模型檔案即可。
可以搜尋到很多相關中文資料 [1] 。
主要代碼如下:
備注:生成的模型檔案名稱為 svm_model_file
訓練生成模型後,需要使用 訓練集 之外的全新的标記後的圖檔作為 測試集 來對模型進行測試。
本文中的測試實驗如下:
使用一組全部标記為8的21張圖檔來進行模型測試
測試圖檔生成帶标記的特征檔案名稱為 last_test_pix_xy_new.txt
在早期訓練集樣本隻有每字元十幾張圖的時候,雖然對訓練集樣本有很好的區分度,但是對于新樣本測試集基本沒區分能力,識别基本是錯誤的。逐漸增加标記為8的訓練集的樣本後情況有了比較好的改觀:
到60張左右的時候,正确率大概80%
到185張的時候,正确率基本上達到100%
以數字8的這種模型強化方法,繼續強化對數字0~9中的其它數字的模型訓練,最後可以達到對所有的數字的圖檔的識别率達到近乎 100%。在本文示例中基本上每個數字的訓練集在100張左右時,就可以達到100%的識别率了。
模型測試代碼如下:
至此,驗證的識别工作算是完滿結束。
在前面的環節,驗證碼識别 的相關工具集都準備好了。然後對指定的網絡上的動态驗證碼形成持續不斷地識别,還需要另外寫一點代碼來組織這個流程,以形成穩定的黑盒的驗證碼識别接口。
傳入一組驗證碼圖檔
對圖檔進行預處理:去噪,二值等等
切割成4張有序的單字元圖檔
使用模型檔案分别對4張圖檔進行識别
将識别結果拼接
傳回識别結果
然後本文中,請求某網絡驗證碼的http接口,獲得驗證碼圖檔,識别出結果,以此結果作為名稱儲存此驗證圖檔。效果如下:
顯然,已經達到幾乎 100% 的識别率了。
在本算法沒有做任何優化的情況下,在目前主流配置的PC機上運作此程式,可以實作200ms識别一個(很大的耗時來自網絡請求的阻塞)。
後期通過優化的方式可以達到更好的效率。
軟體層次優化
将圖檔資源的網絡請求部分做成異步非阻塞模式
利用好多核CPU,多程序并行運作
在圖檔特征上認真挑選和實驗,降低次元
預計可以達到1s識别10到100個驗證碼的樣子。
硬體層次優化
粗暴地增加CPU性能
粗暴地增加運作機器
基本上,10台4核心機器同時請求,保守估計效率可以提升到1s識别1萬個驗證碼。
如果驗證碼被識别出來後,會有什麼安全隐患呢?
在大家通過上一小節對識别效率有了認識之後,再提到這樣的場景,大家會有新的看法了吧:
12306火車售票網,春節期間早上8:00某車次放出的500張票,1s内全部被搶光,最後發現正常需求的人搶不到票,但是黃牛卻大大的有票
某某手機網站,早上10:00開啟搶購活動,守候了許久的無數的你都铩羽而歸,但是同樣黃牛卻大量有貨
暫先不管後面有沒有手續上的黑幕,在一切手續合法的情況下,隻要通過技術手段識别掉了驗證碼,再通過計算機強大的計算力和自動化能力,将大量資源搶到少數黃牛手中在技術是完全可行的。
是以今後大家搶不到票不爽的時候,可以繼續罵12306,但是不要罵它有黑幕了,而是罵他們IT技術不精吧。
關于一個驗證碼失效,即相當于沒有驗證碼的系統,再沒有其它風控政策的情況下,那麼這個系統對于代碼程式來就就完全如入無人之境。
具體請參考:
Web應用系統的小安全漏洞及相應的攻擊方式
http://www.cnblogs.com/beer/p/4814587.html
通過上面的例子,大家可以看到:
目前确實有一些web應用系統連驗證碼都沒有,隻能任人宰割
即使web應用系統有驗證碼但是難度不夠,也隻能任人宰割
是以,這一塊雖然小,但是安全問題不能忽視。
本文介紹的其實是一項簡單的OCR技術實作。有一些很好同時也很有積極進步意義的應用場景:
銀行卡号識别
身份證号識别
車牌号碼識别
這些場景有具有和本文所研究素材很相似的特點:
字型單一
字元為簡單的數字或字母組合
文字的排列是标準化統一化的
是以如果拍照時原始資料采集比較規範的情況下,識别起來應該難度也不大。
本文隻是選取了一個比較典型的而且比較簡單的驗證碼的識别作為示例,但是基本上能表述出一個識别此類驗證碼的完整流程,可以供大家交流學習。
由于目前全球的IT技術實力參差不齊,現在很多舊的IT系統裡面都存在一些舊的頁面架構,裡面使用的驗證碼也是相當古老,對于當下的一些識别技術來說,完全不堪一擊。比如,我看到一些在校大學生就直接拿自己學校的 教務系統 的驗證碼來 開刀練習 的。
最後,本文特意提出如下倡議:
<dt>對于掌握OCR技術的人</dt>
不要做違法的事,因為目前被抓的“白帽子”的新聞也蠻多的
在不違法的情況下,還是可以向存在漏洞的系統管理者提出善意提醒
以自己的專業知識,多做一些促進社會進步,提升社會生産力的事情,如紙書電子化等等
<dt>對于仍然沿用舊的落後的IT系統的公司或者機構相關人員</dt>
應該盡快認識到事情的嚴重性,趕緊更新自己的系統,或者将這一塊業務傳遞給專門的安全公司
<col>
[1]
LibSVM for Python 使用 http://www.cnblogs.com/Finley/p/5329417.html
[2]
基于SVM的手寫體阿拉伯數字識别.張鴿,陳書開.長沙理工大學計算機通訊工程學院.2005
我估計這樣 長文 絕大部分人是不會有興趣全部看完的。但為了它的内容完整性,還是決定先以整篇的方式發表出來吧。
後面有空再拆分連載吧。
如果對本話題有興趣請持續關注本系列文章。
如果還想進一步讨論相應的技術,請加入QQ群:592109504
手機QQ掃描二維碼:https://mp.weixin.qq.com/s/7RCtZH0ljuF5Ti6jgQxyww
驗證識别合輯技術文章:https://zhuanlan.zhihu.com/p/30871712
後續會有更多幹貨文章,敬請期待。。。