驗證碼做爲一種輔助安全手段在Web安全中有着特殊的地位,驗證碼安全和web應用中的衆多漏洞相比彷佛微不足道,可是千里之堤毀於蟻穴,有些時候若是能繞過驗證碼,則能夠把手動變爲自動,對於Web安全檢測有很大的幫助。php
全自動區分計算機和人類的圖靈測試(英語:Completely Automated Public Turing test to tell Computers and Humans Apart,簡稱CAPTCHA),俗稱驗證碼,是一種區分用戶是計算機和人的公共全自動程序。在CAPTCHA測試中,做爲服務器的計算機會自動生成一個問題由用戶來解答。這個問題能夠由計算機生成並評判,可是必須只有人類才能解答。因爲計算機沒法解答CAPTCHA的問題,因此回答出問題的用戶就能夠被認爲是人類。(from wikipedia)html
大部分驗證碼的設計者都不知道爲何要用到驗證碼,或者對於如何檢驗驗證碼的強度沒有任何概念。大多數驗證碼在實現的時候只是把文字印到背景稍微複雜點的圖片上就完事了,程序員沒有從根本上了解驗證碼的設計理念。程序員
驗證碼的形式多種多樣,先介紹最簡單的純文本驗證碼。web
純文本,輸出具備固定格式,數量有限,例如:ajax
•1+1=? •本論壇的域名是? •今天是星期幾? •複雜點的數學運算
這種驗證碼並不符合驗證碼的定義,由於只有自動生成的問題才能用作驗證碼,這種文字驗證碼都是從題庫裏選擇出來的,數量有限。破解方式也很簡單,多刷新幾回,創建題庫和對應的答案,用正則從網頁裏抓取問題,尋找匹配的答案後破解。也有些用隨機生成的數學公式,好比 隨機數 [+-*/]隨機運算符 隨機數=?,小學生水平的程序員也能夠搞定……算法
這種驗證碼也不是一無可取,對於不少見到表單就來一發的spam bot來講,實在不必單獨爲了一個網站下那麼大功夫。對於鐵了心要在你的網站大量灌水的人,這種驗證碼和沒有同樣。後端
下面講的是驗證碼中的重點,圖形驗證碼。安全
先來講一下基礎:服務器
識別圖形驗證碼能夠說是計算機科學裏的一項重要課題,涉及到計算機圖形學,機器學習,機器視覺,人工智能等等高深領域……網絡
簡單地說,計算機圖形學的主要研究內容就是研究如何在計算機中表示圖形、以及利用計算機進行圖形的計算、處理和顯示的相關原理與算法。圖形一般由點、線、面、體等幾何元素和灰度、色彩、線型、線寬等非幾何屬性組成。計算機涉及到的幾何圖形處理通常有 2維到n維圖形處理,邊界區分,面積計算,體積計算,扭曲變形校訂。對於顏色則有色彩空間的計算與轉換,圖形上色,陰影,色差處理等等。
在破解驗證碼中須要用到的知識通常是 像素,線,面等基本2維圖形元素的處理和色差分析。常見工具爲:
•支持向量機(SVM) •OpenCV •圖像處理軟件(Photoshop,Gimp…) •Python Image Library
支持向量機SVM是一個機器學習領域裏經常使用到的分類器,能夠對圖形進行邊界區分,不過須要的背景知識過高深。
OpenCV是一個很經常使用的計算機圖像處理和機器視覺庫,通常用於人臉識別,跟蹤移動物體等等,對這方面有興趣的能夠研究一下
PS,GIMP就不說了,說多了都是淚啊……
Python Image Library是pyhon裏面帶的一個圖形處理庫,功能比較強大,是咱們的首選。
SVM圖像邊界區分
SVM原理,把數據映射到高維空間,而後尋找可以分割的超平面
識別驗證碼須要充分利用圖片中的信息,才能把驗證碼的文字和背景部分分離,一張典型的jpeg圖片,每一個像素均可以放在一個5維的空間裏,這5個維度分別是,X,Y,R,G,B,也就是像素的座標和顏色,在計算機圖形學中,有不少種色彩空間,最經常使用的好比RGB,印刷用的CYMK,還有比較少見的HSL或者HSV,每種色彩空間的維度都不同,可是能夠經過公式互相轉換。
RGB色彩空間構成的立方體,每一個維度表明一種顏色
HSL(色相飽和度)色彩空間構成的錐體,能夠參考:
https://zh.wikipedia.org/wiki/HSL%E5%92%8CHSV%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4
瞭解到色彩空間的原理,就能夠用在該空間適用的公式來進行像素的色差判斷,好比RGB空間裏判斷兩個點的色差能夠用3維空間中兩座標求距離的公式:
distance=sqrt[(r1-r2)^2+(g1-g2)^2+(b1-b2)^2]
更加直觀的圖片,你們感覺一下:
隨便把一張圖片的每一個像素都映射到RGB色彩空間裏就能得到一個這樣的立方體。
經過對像素顏色進行統計和區分,能夠得到圖片的顏色分佈,在驗證碼中,通常來講使用近似顏色最多的像素都是背景,最少的通常爲干擾點,干擾線和須要識別文字自己。
對於在RGB空間中很差區分顏色,能夠把色彩空間轉換爲HSV或HSL:
第一步: 二值化
所謂二值化就是把不須要的信息統統去除,好比背景,干擾線,干擾像素等等,只剩下須要識別的文字,讓圖片變成2進制點陣。
第二步: 文字分割
爲了能識別出字符,須要對要識別的文字圖圖片進行分割,把每一個字符做爲單獨的一個圖片看待。
第三步:標準化
對於部分特殊的驗證碼,須要對分割後的圖片進行標準化處理,也就是說盡可能把每一個相同的字符都變成同樣的格式,減小隨機的程度
最簡單的好比旋轉還原,複雜點的好比扭曲還原等等
第四步:識別
這一步能夠用不少種方法,最簡單的就是模板對比,對每一個出現過的字符進行處理後把點陣變成字符串,標明是什麼字符後,經過字符串對比來判斷類似度。
在文章的後半部分會詳細解釋每步的各類算法
對於大部分彩色驗證碼,經過判斷色差和像素分佈都能準確的把文字和背景分離出來,經過PS等工具把圖片打開,用RGB探針對文字和背景圖的顏色分別測試,在測試多張圖片後,很容易能夠發現文字和背景圖的RGB差距老是大於一個固定的閾值,即便每次圖片的文字和背景顏色都會變化,好比:
新浪和discuz的驗證碼
經過對文字部分和干擾部分取樣能夠發現,文字部分的R、G值通常在100左右,B值接近255,可是背景干擾的R、G值則大大高於文字部分,接近200,比較接近文字輪廓部分的像素的RG值也在150以上。經過程序遍歷一遍像素就能夠徹底去掉背景。
Discuz的驗證碼同理
對於一些和文字顏色相同可是較爲分散和單一的干擾像素點,咱們能夠用判斷相鄰像素的方法,對於每一個點判斷該點和相鄰8個點的色差,若色差大於某個值,則+1,若是周圍有超過6個點的色差都比較大,說明這個點是噪點。對於圖像邊界的一圈像素,周圍沒有8個像素,則通通清除,反正文字都在圖片的中間位置。
以下圖:假如當前像素的座標是x,y 圖形座標系的原點是圖像的左上角
干擾線對於識別驗證碼增長了一些難度,不過干擾線只有很小的概率會以大角度曲線的方式出現,大部分時間仍是小角度直線,去除算法能夠參考http://wenku.baidu.com/view/63bac64f2b160b4e767fcfed.html
對於1個像素粗細的干擾線,在字符爲2個像素以上的時候,能夠用去噪點算法做爲濾鏡,多執行幾回,就能夠完美的把細干擾線去掉。
對於像素數比干擾點稍大的干擾色塊,能夠採用的算法有:
種子填充算法能夠方便的計算出任意色塊的面積,對於沒有粘連字符或者粘連可是字符每一個顏色不同的驗證碼來講,去除干擾色塊的效果很好,你只須要大概計算一下最小的和最大的字符平均佔多少像素,而後把這段區間以外像素數的色塊排除掉便可。
上下左右4個方向填充還有8個方向填充的不一樣
判斷顏色分佈:
對於大多數彩色驗證碼來講,文字基本在圖片中心的位置,每一個字符自己的顏色是同樣的,也就是說對於文字來講,同一種顏色基本都集中在一個固定的區域範圍內,經過統計圖片中的像素,按近似顏色分組,同時分析每一個顏色組在圖片中的分佈範圍,假如說有一種顏色大部分像素都在圖片邊緣,那麼這個顏色確定不屬於要識別的字符,能夠去掉。
對於干擾線,並無一種十分有效的方式能徹底去除而且不影響到文字,不過若是可以成功分割字符的話,少許干擾線對於識別率影響不大。
破解驗證碼的重點和難點就在於可否成功分割字符,這一點也是機器視覺裏的一道難題,對物件的識別能力。對於顏色相同又徹底粘連的字符,好比google的驗證碼,目前是無法作到5%以上的識別率的。不過google的驗證碼基本上人類也只有30%的識別率
對於字符之間徹底沒有粘連的驗證碼,好比這個->_->
分割起來是很是的容易,用最基本的掃描線法就能夠分割,好比從最左側開始從上到下(y=0---|||||y=n)掃描,若是沒有遇到任何文字的像素,就則往右一個像素而後再掃描,若是遇到有文字像素存在,就記錄當前橫座標,繼續向右掃,忽然沒有文字像素的時候,就說明到了兩個字符直接的空白部分,重複這個步驟再橫向掃描就能找到每一個字符最邊緣4個像素的位置,而後能夠用PIL內建的crop功能把單獨的字符摳出來。
對於有少量粘連可是隻是在字符邊角的地方重疊幾個像素的驗證碼,能夠用垂直像素直方圖的統計方法分割。以下圖:
圖上半部分是垂直像素直方圖的一種直觀展現,假如圖片寬度爲100像素,則把圖片切割爲100個1像素的豎線,下面的紅色部分爲當前x座標上全部黑色像素的總和。這麼一來能夠很容易的經過直方圖的波峯波谷把4個字母分割開。圖片的下半部分是掃描線分隔法,由於干擾線和字符旋轉的存在,只有M和5直接纔出現了連續的空白部分。
除了垂直像素直方圖,還能夠從不一樣的角度進行斜線方向的像素數投影,這種方式對於每次全體字符都隨機向一個角度旋轉的驗證碼效果很好。對於每次字符大小和數量都同樣的驗證碼還能夠用平均分割法,也就是直接先把中間的文字部分總體切出來,而後按寬度平均分紅幾份,這種方式對字符粘連比較多用其餘方式很差分割的驗證碼頗有用,以前的megaupload的3位字母驗證碼就是經過這種方式成功分割的。
另外對於彩色的驗證碼,還能夠用顏色分割,好比12306的:
12306的驗證碼,每一個字符顏色都不同,真是省事啊。
做爲驗證碼識別裏的難點,分割字符還有不少種算法,包括筆畫分析曲線角度分析等等,不過即使如此,對粘連的比較厲害的字符仍是很難成功的。
標準化的意思是指對於同一個字符,儘量讓每次識別前的樣本都一致,以提升識別率。而驗證碼設計者則會用隨機旋轉,隨機扭曲還有隨機字體大小的方式防止字符被簡單方法識別。
還原隨機旋轉的字符通常採用的是旋轉卡殼算法:
此算法很是簡單,對一張圖片左右各旋轉30度的範圍,每次1度,旋轉後用掃描線法判斷字符的寬度,對於標準的長方形字體,在徹底垂直的時候確定是寬度最窄的。嗯?納尼?上面的圖是中間的最窄?好像的確是這樣,不過只要每次旋轉後的結果都同樣,對於識別率不會有影響。
扭曲還原的算法比較蛋疼,效果也不怎麼樣(其實我不會),不過若是識別算法好的話,對扭曲的字符只要人能認出來,識別率也能夠達到接近人類的水準。
還有一些經常使用到的算法,對於提升識別率和減小樣本數量有必定幫助:
腐蝕算法的原理有點像剝洋蔥,從最外層沿着最外面的一層像素一圈一圈的去掉,直到裏面只剩下一層像素爲止。腐蝕算法裏面須要用到另外一個算法,叫作凸包算法,用來找一堆像素點裏面最外圍的一層。
最後就是把字符變成統一大小,通常而言是把所有字符都縮到和驗證碼裏出現過的最小的字符一個大小。
詳情請自行google……
分割算法差很少就到這裏了,都是一些比較基礎的內容。下面是最終的識別。
其實到了這一步,單獨的字符已經分離出來了,能夠訓練tesseract ocr來識別了,樣本數量多的話,識別率也是很高的。不過在這裏仍是要講一下,如何本身來實現識別過程。
第一步,樣本如今應該已是一個矩陣的形式了,有像素的地方是1,背景是0,先肉眼識別一下,而後把這個矩陣轉換爲字符串,創建一個鍵值對,標明這串字符串是什麼字符。以後就只須要多蒐集幾個一樣字符的不一樣字符串變形,這就是製做模板的過程,。
蒐集了足夠多的模板後,就能夠開始識別了,最簡單的方法:漢明距離,可是若是字符有少量扭曲的話,識別率會低的離譜。對比近似字符串用的最多通常是 編輯距離算法(Levenshtein Distance),具體請本身google。
兩種算法的差異在於,對一樣兩個字符串對比10010101和10101010,漢明距離是6,可是編輯距離是2。
最後一種最NB的識別算法,就是神經網絡,神經網絡是一種模擬動物神經元工做模式的算法,神經網絡有多種不一樣的結構,可是基本架構分爲輸入層,隱含層和輸出層,輸入和輸出均爲二進制。
對於驗證碼識別來講,輸入和輸出節點不宜過多,由於多了很慢……因此若是樣本矩陣爲20x20 400個像素的話,須要對應的也要有400個輸入節點,所以咱們須要對整個矩陣提取特徵值,好比先橫向每兩個數字XOR一下,而後再豎向每兩個數字XOR。
Python有不少封裝好的神經網絡庫,你所須要的只是把特徵值輸入神經網絡,再告訴他你給他的是什麼(字符),這樣多喂幾回以後,也就是訓練的過程,隨着訓練的進行,神經網絡的內部結構會改變,逐漸向正確的答案靠攏。神經網絡的優點是,對於扭曲的字符識別成功率很是高。另外神經網絡在信息安全中還能夠起到不少其餘做用,好比識別惡意代碼等等。
動畫驗證碼
有些不甘寂寞的程序員又玩出了些新花樣,好比各類GIF甚至flv格式的動畫驗證碼,下面我來分析一下騰訊安全中心的GIF驗證碼。
晃來晃去的看似很難,放慢100倍一幀一幀再看看?
基本上每幀都有一個字符和其餘的分開,用最簡單的掃描法就能分割出來。
剩下的就很輕鬆了,旋轉還原以後,先填充內部空白,縮小細化以後作成模板對比,識別率怎麼也得有90%了。
本來一張圖就能搞定的事情,恰恰給了咱們8張圖,並且每張圖還有一點區別,無緣無故增大了不少信息量。
另外就是一些所謂的高用戶體驗的驗證碼,好比freebuf的:
拖動解鎖按鈕會觸發執行一段js,生成一串隨機字符串,ajax給後端程序判斷。
破解方式就當留給你們的思考題了,假如我想刷評論的話,怎麼辦。
還有就是聲音驗證碼的識別,如今不少驗證碼爲了提升用戶體驗和照顧視覺障礙的用戶,都有聲音驗證碼,通常來講是機器生成一段讀數字的語音。可是在這方面上不少程序員都偷懶了,預先找了10個數字的聲音錄音,而後生成的時候把他們隨機拼到一塊兒,結果就是這樣:
前3秒爲語音提示,後面的是數字,有沒有發現什麼?
聲音也是能夠作成模板的哦
最後就是應該怎麼樣去設計驗證碼
•總體效果 •字符數量必定範圍內隨機 •字體大小必定範圍內隨機 •波浪扭曲(角度方向必定範圍內隨機) •防識別 •不要過分依賴防識別技術 •不要使用過多字符集-用戶體驗差 •防分割 •重疊粘連比干擾線效果好 •備用計劃 •一樣強度徹底不一樣的一套驗證碼