細說驗證碼安全 —— 測試思路大梳理
1 前言
在安全領域,驗證碼主要分為兩大類:操作驗證碼 和 身份驗證碼
雖然都是驗證碼,但是這兩者所承擔的職責卻完全不同。
操作驗證碼,比如登錄驗證碼,主要用來 區分人與機器 ,在某種程度上屬於圖靈測試
身份驗證碼,主要用來判斷賬號歸屬人,解決信任問題,所以更恰當的叫法是 認證碼
由於兩者的分工和定位不同,所衍生出的安全問題、所關注的安全點也有所不同。
本文將烏雲中所有驗證碼相關案例提煉、分析並匯總,得出一些可重用的方法和經驗。
希望本文能給讀者帶來一些微小的幫助,當你遇到驗證碼相關業務時,能有一個比較完整的測試和審計思路。
PS:在這一過程中,我居然發現,烏雲中的驗證碼的主要案例類型,與我平時所遇到的案例類型,基本是大致相同的(8大類和5大類)
原來各地的程序員犯的錯都是類似的呀 :)
2 操作驗證碼安全
操作驗證碼,主要是為了解決三個問題:
1、賬戶暴力破解
2、高頻次的接口訪問
3、敏感操作二次確認(CSRF)
實際上這三個問題,都屬於 人機區分問題,即 這個操作、請求到底是不是人為地、自願地發出的?
驗證碼安全,圍繞下面幾點展開:
1、驗證碼可重用 (特定賬戶暴力破解、CSRF)
2、驗證碼可識別 (特定賬戶暴力破解)
3、驗證碼在客戶端生成、顯示、校驗 (特定賬戶暴力破解、CSRF)
4、空驗證碼繞過 (特定賬戶暴力破解、CSRF)
5、驗證碼數量有限 (特定賬戶暴力破解)
6、是否校驗客戶端可控 (特定賬戶暴力破解、CSRF)
7、驗證碼可預測 (特定賬戶暴力破解)
8、錯誤超過一定次數才開啟驗證碼 (撞庫)
我將烏雲上所有的驗證碼案例匯總分析,共有63個相關案例,得到如下統計結果:
0x01 驗證碼可重用
這是驗證碼安全里最常見的一類安全問題,也是最容易遺漏的一類
一般來說,驗證碼是與Session綁定的,Session生成時,往往也伴隨着驗證碼的生成和綁定。
在訪問頁面時,接口的請求和驗證碼的生成通常是異步進行的,這使得兩個功能變得相對獨立。也就意味着我們如果僅請求接口,而不觸發驗證碼的生成,那么驗證碼就不會變化。
並且在考慮安全時,開發人員的關注點往往在 驗證碼校驗 是否通過,通過則進入業務流程,不通過則重新填寫,而忽視了 這個用戶是否按照既定的業務流程在走(接口訪問與驗證碼生成是否同時進行),驗證碼是否被多次使用了。
理論上來講,任何驗證碼只能使用一次或幾次,否則就可能導致安全問題
- 案例1 (驗證碼輸入正確時,未銷毀重置)
當用戶輸入正確的驗證碼時,程序認為其通過了校驗,直接進入了業務流程,忽視了驗證碼銷毀重置的問題。
我們可以在輸入了正確驗證碼后,不斷重用這一驗證碼,這導致了 特定賬戶暴力破解的問題
WooYun這方面的案例有27個
http://www.anquan.us/static/bugs/wooyun-2015-0116594.html 驗證正確,未銷毀
http://www.anquan.us/static/bugs/wooyun-2016-0169672.html 正確,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-0164315.html 正確,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-0111128.html 正確,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-0110497.html 校驗正確后,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-0102697.html 校驗正確后,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-099708.html 校驗正確后,未銷毀
http://www.anquan.us/static/bugs/wooyun-2015-093065.html 校驗正確后,未銷毀
http://www.anquan.us/static/bugs/wooyun-2014-087890.html 錯誤,未銷毀,可爆破
http://www.anquan.us/static/bugs/wooyun-2014-085942.html 校驗正確后,未銷毀
http://www.anquan.us/static/bugs/wooyun-2014-084180.html 同上
http://www.anquan.us/static/bugs/wooyun-2014-083092.html 同上
http://www.anquan.us/static/bugs/wooyun-2014-083274.html 同上
http://www.anquan.us/static/bugs/wooyun-2014-082783.html 同上
http://www.anquan.us/static/bugs/wooyun-2014-074661.html
http://www.anquan.us/static/bugs/wooyun-2014-070959.html
http://www.anquan.us/static/bugs/wooyun-2014-056990.html 輸入錯誤時銷毀,正確時不銷毀
http://www.anquan.us/static/bugs/wooyun-2014-050862.html
http://www.anquan.us/static/bugs/wooyun-2014-049064.html
http://www.anquan.us/static/bugs/wooyun-2013-046547.html 異步機制請求驗證碼,未銷毀
http://www.anquan.us/static/bugs/wooyun-2013-028024.html 同上
http://www.anquan.us/static/bugs/wooyun-2013-025053.html 未銷毀
http://www.anquan.us/static/bugs/wooyun-2013-020460.html
http://www.anquan.us/static/bugs/wooyun-2012-013915.html 未銷毀
http://www.anquan.us/static/bugs/wooyun-2012-06226.html
http://www.anquan.us/static/bugs/wooyun-2011-03450.html
- 案例2 (驗證碼輸入錯誤時,未銷毀)
當用戶輸入錯誤的驗證碼,而程序沒有將驗證碼重置時,也會存在安全隱患
不過驗證碼的爆破,有什么意義呢?我們本來就可以看到呀
當一個敏感操作的CSRF存在驗證碼防御,且驗證碼比較弱時,我們就可以用js寫腳本來爆破,繞過防御
0x02 驗證碼可識別
- 案例 1(驗證碼過於簡單)
這個屬於最簡單的驗證碼,過於簡單、清晰、可識別性高,可以編寫程序進行識別,導致驗證碼防御體系失效
WooYun中共有7個類似案例:
http://www.anquan.us/static/bugs/wooyun-2016-0204186.html
http://www.anquan.us/static/bugs/wooyun-2016-0194576.html
http://www.anquan.us/static/bugs/wooyun-2016-0176919.html
http://www.anquan.us/static/bugs/wooyun-2015-0120388.html
http://www.anquan.us/static/bugs/wooyun-2012-012722.html
http://www.anquan.us/static/bugs/wooyun-2012-011765.html
http://www.anquan.us/static/bugs/wooyun-2012-010851.html
實際上,比這個更難識別,更復雜的驗證碼,也有一些准確率較高的識別方法,我們在測試時把握好效果與成本的平衡即可
0x03 客戶端生成/顯示/校驗
- 案例1(客戶端生成驗證碼文本,然后在服務端請求對應的img)
程序在客戶端生成驗證碼文本,然后向服務端請求該文本對應的 img,導致我們在客戶端直接拿到驗證碼
程序在客戶端生成驗證碼文本,然后加圖層生成img
- 案例2 (客戶端生成驗證碼,並且輸出到HTML標簽中)
程序在客戶端生成驗證碼,並且輸出到form表單里的html標簽中,可能是為了方便校驗?
WooYun中共有5個類似案例:
http://www.anquan.us/static/bugs/wooyun-2015-0161823.html
http://www.anquan.us/static/bugs/wooyun-2015-0146767.html
http://www.anquan.us/static/bugs/wooyun-2015-099909.html
http://www.anquan.us/static/bugs/wooyun-2012-06634.html
http://www.anquan.us/static/bugs/wooyun-2012-012829.html
https://xz.aliyun.com/t/4487
- 案例3(服務端生成驗證碼,但將明文文本返回給了客戶端)
驗證碼生成之后,向客戶端返回了驗證碼文本(Cookie、body)
WooYun中共有6個類似案例:
http://www.anquan.us/static/bugs/wooyun-2013-023090.html
http://www.anquan.us/static/bugs/wooyun-2012-010524.html
http://www.anquan.us/static/bugs/wooyun-2012-05151.html
http://www.anquan.us/static/bugs/wooyun-2012-03967.html
http://www.anquan.us/static/bugs/wooyun-2014-075186.html
http://www.anquan.us/static/bugs/wooyun-2014-073811.html
https://xz.aliyun.com/t/4533
0x04 空驗證碼繞過
如果你的代碼是這樣寫的,那就會存在安全問題
if isset($_POST['captcha'])
{
....
}
login();
當驗證碼為空時,不進入驗證碼判斷流程,直接進入業務邏輯
WooYun中有6個類似案例:
http://www.anquan.us/static/bugs/wooyun-2015-0150406.html
http://www.anquan.us/static/bugs/wooyun-2013-028061.html
http://www.anquan.us/static/bugs/wooyun-2013-025065.html
http://www.anquan.us/static/bugs/wooyun-2012-014224.html
http://www.anquan.us/static/bugs/wooyun-2012-08287.html
http://www.anquan.us/static/bugs/wooyun-2014-049531.html
0x05 驗證碼數量有限
當程序使用靜態的圖片,而不是動態生成驗證碼時,圖片的數量將是有限的。
我們可以將其全部取回並計算md5,以此繞過驗證碼機制。
WooYun中有2個類似案例,之前的12306也屬於這種情況
http://www.anquan.us/static/bugs/wooyun-2015-0102178.html
http://www.anquan.us/static/bugs/wooyun-2012-07413.html
0x06 是否校驗可控
天才才能寫出來的驗證碼校驗機制,請求中存在一個字段,來決定是否進行校驗,修改為 false(0) 即可
WooYun中有5個類似案例
http://www.anquan.us/static/bugs/wooyun-2014-071289.html
http://www.anquan.us/static/bugs/wooyun-2013-034367.html
http://www.anquan.us/static/bugs/wooyun-2013-026219.html
http://www.anquan.us/static/bugs/wooyun-2012-014563.html
http://www.anquan.us/static/bugs/wooyun-2014-082981.html
0x07 超過次數才開啟驗證碼
接口在登錄錯誤超過一定次數后才會開啟驗證碼,這種機制要么是基於ip判斷,要么就是基於session判斷,要么是基於賬號判斷
- 案例1 (基於session)
如果是基於Session判斷,我們清空session即可繞過。
WooYun中有1個類似案例
http://www.anquan.us/static/bugs/wooyun-2015-0114450.html
- 案例2 (基於ip)
如果是基於ip判斷,我們可以嘗試ip是否可以偽造,或者使用代理池
WooYun中有1個類似案例
http://www.anquan.us/static/bugs/wooyun-2014-080327.html
- 案例3 (基於賬號)
服務端的限制僅針對於特定賬號,比如某賬戶錯誤5次以上開啟驗證碼。
這種情況下雖然無法暴力破解特定賬戶,但是仍然可以實施撞庫攻擊
WooYun中有2個類似案例
http://www.anquan.us/static/bugs/wooyun-2015-0149748.html
http://www.anquan.us/static/bugs/wooyun-2016-0193985.html
0x08 驗證碼可預測
當驗證碼與時間戳等因素強相關時,就不再具有隨機性的屬性,導致驗證碼形同虛設。
WooYun中有1個類似案例
http://www.anquan.us/static/bugs/wooyun-2015-0115041.html
3 身份驗證碼安全
身份驗證碼主要是為了驗證操作人身份,然后進行 密碼修改、賬戶變更、重要操作等功能。
而這類驗證碼主要牽扯到5類安全問題:
1、驗證碼返回給客戶端
2、業務流程缺陷
3、驗證碼無時間間隔限制
4、驗證碼可爆破
5、驗證碼在客戶端生成
將烏雲中的案例去重、去無關案例后,有41個身份驗證碼的案例,分布如下:
0x01 驗證碼返回客戶端
服務器將驗證碼明文返回給客戶端,本來覺得這種錯誤比較低級,沒想到這樣的案例還挺多。
大致有三種可能,一種是驗證碼校驗在客戶端進行,這種錯誤太低級了,可能性不大。
另一種情況:
1、客戶點擊獲取驗證碼
2、程序生成一個隨機驗證碼,將參數拼接之后,提交給短信API
3、客戶端需要判斷是否發送成功,所以程序將短信API返回的內容交給了客戶端
作為一個短信API,很有可能會在response中包含了發送的短信內容,導致驗證碼的泄露
最后一種情況,開發寫API的時候,為了方便調試,返回了這些信息,后來忘刪了...
- 案例
WooYun中有15個類似案例
http://www.anquan.us/static/bugs/wooyun-2016-0179467.html
http://www.anquan.us/static/bugs/wooyun-2016-0172266.html
http://www.anquan.us/static/bugs/wooyun-2015-0139468.html
http://www.anquan.us/static/bugs/wooyun-2014-085124.html
http://www.anquan.us/static/bugs/wooyun-2014-082114.html
http://www.anquan.us/static/bugs/wooyun-2014-078687.html
http://www.anquan.us/static/bugs/wooyun-2014-066510.html
http://www.anquan.us/static/bugs/wooyun-2014-049813.html
http://www.anquan.us/static/bugs/wooyun-2014-049547.html
http://www.anquan.us/static/bugs/wooyun-2013-042464.html
http://www.anquan.us/static/bugs/wooyun-2013-024195.html
http://www.anquan.us/static/bugs/wooyun-2013-022009.html
http://www.anquan.us/static/bugs/wooyun-2013-019668.html
http://www.anquan.us/static/bugs/wooyun-2014-085124.html
http://www.anquan.us/static/bugs/wooyun-2014-082114.html
0x02 業務流程缺陷
涉及到驗證碼的業務,通常都分為多步進行,比如 修改手機號功能:認證原手機號 -> 填寫新手機號
當下一步的業務,沒有校驗上一步的認證是否成功時,就會存在邏輯缺陷繞過。
- 案例1 (修改response繞過)
填寫手機驗證碼時填任意值,然后修改請求的response包中的標識字段,將其修改為true,即可繞過
實際上這種問題,本質上也是業務流程的邏輯缺陷問題。
雖然驗證碼的校驗在服務端進行,但是下一步的業務,並沒有校驗上一步的認證是否成功,兩者之間是獨立的
這就導致我們可以修改response,讓客戶端直接跳入下一次邏輯,我們也可以審計源碼,直接找出下一步的url
WooYun中有5個類似案例
http://www.anquan.us/static/bugs/wooyun-2015-0151201.html
http://www.anquan.us/static/bugs/wooyun-2015-0120951.html
http://www.anquan.us/static/bugs/wooyun-2015-0119252.html
http://www.anquan.us/static/bugs/wooyun-2015-0104509.html
http://www.anquan.us/static/bugs/wooyun-2015-090379.html
- 案例2 (手機號合法性)
在驗證碼校驗過程中,程序應嚴格檢查對應關系,即 接收驗證碼的手機號,是否是該賬戶對應的手機號
如果不存在這處對應關系校驗,則會衍生出各種邏輯問題,比如用自己的手機通過驗證,然后修改其它人的信息
其實這種情況下,也是存在業務流程缺陷的問題。下一步的業務,並沒有校驗上一步業務中,手機號是否是屬於該賬戶的
http://www.anquan.us/static/bugs/wooyun-2015-0102205.html
http://www.anquan.us/static/bugs/wooyun-2011-03099.html
http://www.anquan.us/static/bugs/wooyun-2014-080315.html
0x03 驗證碼無時間間隔限制
服務端對用戶請求短信的頻次沒有時間間隔限制,或者是在客戶端限制,可導致短信資源濫用
沒有基於session、ip、賬戶的限制,屬於完全無限制的情況
http://www.anquan.us/static/bugs/wooyun-2012-010102.html
http://www.anquan.us/static/bugs/wooyun-2012-010556.html
http://www.anquan.us/static/bugs/wooyun-2012-04876.html
http://www.anquan.us/static/bugs/wooyun-2012-04771.html
http://www.anquan.us/static/bugs/wooyun-2012-04166.html
http://www.anquan.us/static/bugs/wooyun-2012-04022.html
http://www.anquan.us/static/bugs/wooyun-2011-01188.html
http://www.anquan.us/static/bugs/wooyun-2011-03485.html
0x04 驗證碼可爆破
- 案例1 (完全無限制)
當驗證碼太弱(4-6位數字),且服務器沒有錯誤次數限制時,則會存在可爆破的問題
WooYun中有7個類似案例
http://www.anquan.us/static/bugs/wooyun-2015-0155994.html
http://www.anquan.us/static/bugs/wooyun-2013-017242.html
http://www.anquan.us/static/bugs/wooyun-2013-016896.html
http://www.anquan.us/static/bugs/wooyun-2012-016179.html
http://www.anquan.us/static/bugs/wooyun-2013-031605.html
http://www.anquan.us/static/bugs/wooyun-2013-040908.html
http://www.anquan.us/static/bugs/wooyun-2012-012377.html
- 案例2 (限制覆蓋不全)
以重置密碼業務為例:用戶輸入手機驗證碼 -> 用戶提交新密碼
為了解決業務流程綁定的問題,通常兩個步驟的參數中都會帶有驗證碼。
開發人員往往只注意到第一個接口,而忽視了第二個接口。此時,在第一個頁面中使用自己的手機號通過驗證,第二個頁面中修改為他人手機號並爆破
WooYun中有1個類似案例:
http://www.anquan.us/static/bugs/wooyun-2015-0133289.html
0x05 驗證碼在客戶端生成
這種情況下,客戶端生成一個驗證碼發送給服務端,服務端將這個驗證碼拼接,然后請求短信API發送短信
天才才能想出來的辦法
WooYun中有2個類似案例
http://www.anquan.us/static/bugs/wooyun-2014-086716.html
http://www.anquan.us/static/bugs/wooyun-2013-022378.html
4 參考
幾乎所有漏洞案例都來自烏雲、個別案例來自先知 :)
pdf版本鏈接https://xzfile.aliyuncs.com/upload/affix/20190815235348-def019d4-bf74-1.pdf







