在工作过程中,接手一个项目,项目的登录验证码部分是使用js写的,由js随机生成一个字符串当作验证码。我觉得这样做其实是不合理的。
验证码是,用来区分对方到底是人还是计算机程序而设计的一种验证措施。主要用于阻止垃圾信息,保护网站注册,在线投票系统刷票等等。因此验证码的实质是:用于区分行为方到底是人还是程序。
如果使用js来生成验证码,那么生成方法会暴露给所有人,这不是最主要的。
js生成验证的过程大概是:随机生成一个字符串(当然,在现实的时候,可能会加入css控制其外观,使其眼花缭乱)保存到一个全局变量,当用户提交表单的时候,js会验证用户输入的验证码部分是否和全局变量中保存的字符串相等,如果相等,则将表单”剩余”部分提交给服务器。
问题就在这里,提交表单的时候,并没有提交验证码部分,服务器端根本不关心验证码部分,如果有黑客想写程序进攻系统,在提交表单时,将除验证码之外的“剩余”部分提交给服务器,则完全绕过了验证码。那么,验证码就成了一个“自欺欺人”的,只为了表现给用户看,实际没有作用的废物。
在验证用户输入的验证码时,使用的方法:
这种设计,对计算机程序,完全没有作用。下面我将演示一下为何对程序没有作用。
我们写一段Java程序,使用HttpClient来发送http请求,提交表单:
这里,我们先使用get方法请求一下登录页面:
在控制台打印出的是;
<html>
<head>
<title>运营安全服务</title>
<link href="/twm-manager/css/login.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/twm-manager/js/jquery-1.4.2.js"></script>
<script type="text/javascript">
//验证码刷新
function newImg() {
$("#authImg").src="/twm-manager/validCode?time="+new Date();
}
function init(){
document.getElementById("userName").focus();
if("null" == "null" || "null" == ""){
$("#msg_tr").hide();
}else{
$("#msg_tr").show();
$("#showErrorMsg").html("null");
}
createCode();
}
var objectPath = "/twm-manager";
var code ; //在全局 定义验证码
function createCode(){
code="";
var codes = new Array(4);//验证码的长度
var checkCode = document.getElementById("divCode");
var selectChar = new Array(2,3,4,5,6,7,8,9,'a','b','c','d','e','f','g','h','k','m','n','p','q','r','s','t','u','v','w','x','y','z');//所有候选组成验证码的字符,当然也可以用中文的
var colors = new Array("Red","Green","Gray","Blue","Maroon","Aqua","Fuchsia","Lime","Olive","Silver");
for(var i=0;i<codes.length;i++)
{
var charIndex = Math.floor(Math.random()*30);
codes[i] =selectChar[charIndex];
}
var spans = document.getElementById("divCode").all;
for(var i=0;i<spans.length;i++)
{
spans[i].innerHTML= codes[i];
code+=codes[i];
spans[i].style.color = colors[Math.floor(Math.random()*10)]; //随机设置验证码颜色
spans[i].style.fontSize=14;
}
}
function findPassword(){
var url ='/twm-manager/login.do?method=findPasswordBack';
window.open(url);
}
function validateCode(){
var userName = $("#userName").val();
if(userName.length <= 0){
document.all.userName.focus();
$("#msg_tr").show();
$("#showErrorMsg").html("用户名不能为空!");
return false;
}
var accountPassword = $("#accountPassword").val();
if(accountPassword.length <= 0){
document.all.accountPassword.focus();
$("#msg_tr").show();
$("#showErrorMsg").html("密码不能为空!");
return false;
}
var inputCode = $("#checkCode").val();
if(inputCode.length <= 0)
{
document.all.checkCode.focus();
$("#msg_tr").show();
$("#showErrorMsg").html("验证码不能为空!");
//createCode();//刷新验证码
return false;
}else if(inputCode != code ){
document.all.checkCode.value="";
document.all.checkCode.focus();
$("#msg_tr").show();
$("#showErrorMsg").html("验证码不正确!");
createCode();//刷新验证码
return false;
}else{
return true;
}
}
</script>
</head>
<body class="body" onload="init()">
<form id="loginForm" name="loginForm" action="/twm-manager/login.do?method=login" method="post" onsubmit="return validateCode();">
<input type="hidden" name="background" value="background"/>
<input type="hidden" name="token" value="bfront"/>
<div class="main">
<div class="left_info">
<div class="left_info_cont">
<p id="moduleNameP">
温馨提示
</p>
<p style="font-size: 12px; font-weight: normal;"
id="moduleSummaryP">
为了保证您能更好的使用该系统,请您输入正确的用户名与密码。
</p>
</div>
</div>
<div class="mid_info" id="errMsgPosFlag">
<div class="tit_biaoti">
客户安全服务管理系统
</div>
<div class="titleLog"></div>
<div style="position: relative" style="padding-top:14px;">
<table width="98%" border="0" cellpadding="0" cellspacing="0"
class="table_style">
<tr id="msg_tr">
<td width="31%" align="right">
提 示:
</td>
<td width="69%" align="left">
<font color="red"><span id="showErrorMsg"></span></font>
</td>
</tr>
<tr>
<td width="31%" align="right">
用户名:
</td>
<td width="69%" align="left">
<input type="text" name="userName" tabindex="1" id="userName" value=""
maxlength="20" class="input_w" />
</td>
</tr>
<tr>
<td align="right">
密 码:
</td>
<td align="left">
<input type="password" name="accountPassword" tabindex="2"
id="accountPassword" maxlength="20"
class="input_w" />
</td>
</tr>
<tr>
<td align="right">
验证码:
</td>
<td align="left">
<span style="float:left; padding-right:5px"><input type="text" style="width:100px; height:19px;" id="checkCode" name="checkCode" size="11" /></span>
<div id="divCode" onclick="javascript:createCode();" style="background-color:White; width:50px; height:6px; padding:5px; text-align:center; vertical-align:middle; letter-spacing:5px; border:solid 1px blue"><span></span><span ></span><span ></span><span ></span></div>
</td>
</tr>
<tr>
<td align="center" colspan="2" style="padding-top: 0px;">
<input class="botton" type="submit" id="loginSubmit" value="登 录"/>
<input class="botton" type="reset" id="resetBut" value="重 置" />
<input class="botton" type="button" value="找回密码" onclick="findPassword()"/>
</tr>
</table>
</div>
</div>
<div class="right_info"
style="padding-top: 90px; color: #ffffff; font-weight: normal; font-family: 华文行楷;">
<h1 style="padding-left: 30px; padding-right: 0px;">
完善您的安全
</h1>
</div>
<div
style="text-align: left; position: absolute; margin-top:250px; left: 42%">
<div style="font-size: 16px; font-weight: bold;">
运营安全服务中心
</div>
<br />
<div style="font-size: 12px;">
版权所有@2009集团公司 <br/>
<span style="margin-left:130px;"></span>
</div>
</div>
</form>
</body>
</html>
这里正是登录页面的html源代码。
接下来,我们使用post方法模拟提交表单:
我们查看登录页面的form,找到form提交的action:
这里action为login.do?method=login
@Test
public void testPost() {
String url = "http://192.168.73.245:8080/twm-manager/login.do?method=login";
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
List<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("userName", "msy"));
formparams.add(new BasicNameValuePair("accountPassword", "test123"));
UrlEncodedFormEntity uefEntity;
Header set_cookie = null;
Header location = null ;
try {
uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
httppost.setEntity(uefEntity);
System.out.println("executing request " + httppost.getURI());
HttpResponse response;
response = httpclient.execute(httppost);
HttpEntity entity = response.getEntity();
System.out.println("response-->" + response.toString());
set_cookie = response.getFirstHeader("Set-Cookie");
System.out.println("set_cookie : "+set_cookie.getValue());
if(entity != null){
System.out.println(EntityUtils.toString(entity));
}
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
httpclient.getConnectionManager().shutdown();
}
}
注意:这里我们form表单的参数只有用户名和密码。
控制台打印出获取到的信息:
executing requesthttp://192.168.73.245:8080/twm-manager/login.do?method=login
response-->HTTP/1.1 200 OK[Server: Apache-Coyote/1.1, Set-Cookie: JSESSIONID=AD77010065B4709854D99E78E6AB6A5C;Path=/twm-manager, Content-Type: text/html;charset=utf-8, Content-Length: 824,Date: Thu, 13 Feb 2014 09:32:45 GMT]
set_cookie :JSESSIONID=AD77010065B4709854D99E78E6AB6A5C; Path=/twm-manager
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>自服务后台管理</title>
</head>
<frameset rows="95,*,25" cols="*" framespacing="0" border="0" frameborder="no">
<frame src="/twm-manager/frame/topFrame.jsp" name="topFrame" frameborder="no" scrolling="NO" >
<frameset cols="0,*" name="menuallFrame" id="menuallFrame">
<frame src="/twm-manager/frame/leftFrame.jsp" name="leftFrame" id="leftFrame">
<frame src="/twm-manager/manager/portal/welcome.jsp" name="mainFrame" id="mframe" frameborder="no" class="naviBar" style="background-color: #f2f8ff;" >
</frameset>
<frame src="/twm-manager/frame/bottomFrame.jsp" name="bottomFrame" frameborder="no" scrolling="NO" noresize="noresize" >
</frameset>
<noframes><body>
</body></noframes>
</html>
这里得到的是什么,就是登录成功后的首页,从源代码中可以看出首页使用了frame进行布局。可是这里我们并没有涉及到验证码。因此,使用js生成的验证码对计算机程序来说,是无效的。
正确的验证码的应该是,后台生成验证码图片,以流的形式输到前台页面。在提交表单的时候,连验证码一同提交到后台,由后台判断验证码是否正确。这样正确起到验证码的作用。