验证码的设计分析

本文探讨了使用JavaScript生成验证码的潜在安全问题,指出仅靠JS验证无法有效防止恶意程序攻击,因为验证码信息未提交至服务器。作者通过Java程序演示了如何绕过此类验证码,强调了正确实现验证码的方式应包括后端生成并校验验证码图片,以确保其效用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在工作过程中,接手一个项目,项目的登录验证码部分是使用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生成的验证码对计算机程序来说,是无效的。

 

    正确的验证码的应该是,后台生成验证码图片,以流的形式输到前台页面。在提交表单的时候,连验证码一同提交到后台,由后台判断验证码是否正确。这样正确起到验证码的作用。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值