几种验证码方式对比
今天要给新公司的网站做一个安全性比较高的验证码,因为最近网站一直被人发垃圾贴。
方案一:以前公司的验证码方案非常原始,就是使用的img.src = verifycode.aspx?code=4321这样来做,4321就是图片里的数字,这样做很无赖,因为编辑要求这个验证码的验证工作在客户端就能做了,所以就直接把code写在了客户端以明文形式存在。这种形式随便哪个会写正则的人都能写出个机器人来发垃圾贴。
方案二:也就是网上很通用的.net生成验证码方案,图片的地址指向一个页面,页面write一段content-type 为img的流到客户端,然后将cookie写入客户端,cookie是被服务器端加密的。这样,在提交页面的时候可以将cookie的加密串和用户输入的验证码一起post到服务器端对证。但是这个方案的严重缺点就是cookie覆盖的问题。这个问题同时暴露于几个大的门户网站,包括网易,新浪,还有soho的注册或登录。比如这个链接:
http://passport.sohu.com/web/reguser
当你打开2个以上的链接的时候,再回过来提交其中非最后一个页面,那么页面的验证码就会被提示不正确。因为最新的cookie已经覆盖了你以前的cookie导致解密的时候不正确。怎么来解决这个问题,今天我思考了一下午,要解决访问验证码并发冲突的问题,就不能将加密数据存储于cookie和session.(其中有位朋友提到存储到session中,但其实session也是基于cookie,而且不是大网站所为,很难负载均衡。下面会提到使用Session).所以,有了我的第三种验证码方案。
方案三:
这又几行我封装的登录验证用户控件,代码解释一下:
第一步:在服务器端随机生成一个4位数字并加密,加密串赋值给一个hidden控件,也就是代码上面的hvimg。
第二步:在window.onload的时候,将一个hidden控件的值动态拼凑在一个链接页面里作为查询字符串,这个页面verifycode.aspx?v=就是用来回发图片的。
第三步:点击看不清楚的链接,客户端发送一个ajax请求给服务器端动态请求一个加密串,然后赋值给hidden控件,并且动态将img的src改变,并成功获取新的验证码图片。
第四步:到服务器端验证,将hidden控件里的值解密后与用户输入的验证码对证。验证是否输入正确。
注:getNum.aspx为ajax请求页面,他只是返回一个加密串,期间涉及urlencode/decode问题,请大家稍微注意下
当然还有中稍微简单点的办法,基本思路是:将加密串存储在hidden内,但是不使用ajax来动态刷新验证码图片,要刷新的话就整个页面postback到服务器端。
设计稍微简单点,但是用户体验差点。
为什么要这样设计呢?
1 防止cookie被覆盖。
2 防止动态请求验证码图片的时候整体刷新页面。
3 防止被识破以及垃圾评论。
另外,我还想说一下在服务器端存储验证码,csdn就是这样(据我分析,并无真凭实据)。
大家可以试验一下:使用IE7,打开csdn页面:http://passport.csdn.net/UserLogin.aspx
在同一tab里再次打开,然后在第一个页面里输入第二次打开的验证码登录,提示登录成功,呵呵,很有意思吧!
原因为什么呢?因为他将两次打开页面的验证码都存储在了服务器端,比如某个Session下的一个hashtable,然后在登录的时候只要匹配能在hashtable里找到这个用户输入的验证码,那么就算验证码正确。
但是如果使用IE6演示,那么演示不出来.使用的IE6,为什么就不能演示成功?那为什么?因为asp.net将新开的2个IE6 视为不同的会话,这样在asp.net里会被分配成不同的asp_net_session_id cookie值,具体你不信的话可以使用fiddler跟踪查看一下cookie。既然被视为不同会话,那么Session里的hashtable都不同,当然演示不成功。
另外,新浪做得验证码特别有特色,居然还可以收听验证码,呵呵,大家可以看看这个链接,到页面尾,输入验证码的地方,有个链接,是”收听验证码“
http://blog.sina.com.cn/s/blog_57ebc672010089rx.html
但是后来被我淘腾得收听到的验证码和实际图片的验证码不一致了,哈哈。
关于验证码的东西今天就研究这么点,希望大家把自己更好的解决方案跟大家分享一下,我也只是抛砖引玉。
如果大家要那个第三个方案的源码,可以给我留言,那个我自己暂时还没有整理好,稍后会奉上。
方案一:以前公司的验证码方案非常原始,就是使用的img.src = verifycode.aspx?code=4321这样来做,4321就是图片里的数字,这样做很无赖,因为编辑要求这个验证码的验证工作在客户端就能做了,所以就直接把code写在了客户端以明文形式存在。这种形式随便哪个会写正则的人都能写出个机器人来发垃圾贴。
方案二:也就是网上很通用的.net生成验证码方案,图片的地址指向一个页面,页面write一段content-type 为img的流到客户端,然后将cookie写入客户端,cookie是被服务器端加密的。这样,在提交页面的时候可以将cookie的加密串和用户输入的验证码一起post到服务器端对证。但是这个方案的严重缺点就是cookie覆盖的问题。这个问题同时暴露于几个大的门户网站,包括网易,新浪,还有soho的注册或登录。比如这个链接:
http://passport.sohu.com/web/reguser
当你打开2个以上的链接的时候,再回过来提交其中非最后一个页面,那么页面的验证码就会被提示不正确。因为最新的cookie已经覆盖了你以前的cookie导致解密的时候不正确。怎么来解决这个问题,今天我思考了一下午,要解决访问验证码并发冲突的问题,就不能将加密数据存储于cookie和session.(其中有位朋友提到存储到session中,但其实session也是基于cookie,而且不是大网站所为,很难负载均衡。下面会提到使用Session).所以,有了我的第三种验证码方案。
方案三:
1<%@ Control Language="C#" AutoEventWireup="true" CodeFile="Vimg.ascx.cs" Inherits="Vimg" %>
2<input id="vtext" runat=server /><asp:Image runat=server ID="vimg" ImageUrl="" /><%--<a href='' onclick='window.location.href=window.location.href;' >看不清楚?</a>--%>
3<script type="text/javascript" src="js/jquery.js"></script>
4<input type=hidden id="hvimg" value="" runat=server />
5<script>
6window.onload = function(){
7 document.getElementById('<%=vimg.ClientID %>').src="verifycode.aspx?v="+document.getElementById('<%=hvimg.ClientID %>').value;
8}
9function getIMG()
10{
11
12 $.ajax({
13 type: "GET",
14 url:"getNum.aspx?timestamp="+new Date(),
15 data:"",
16 success:successfunc
17});
18
19}
20function successfunc (msg)
21{debugger
22 $("#<%=hvimg.ClientID %>").get(0).value = msg;
23 document.getElementById('<%=vimg.ClientID %>').src="verifycode.aspx?v="+document.getElementById('<%=hvimg.ClientID %>').value;
24
25}
26</script>
27<a href="javascript:void(0);" onclick="getIMG();return false;">看不清?</a>
28
29
30
31
2<input id="vtext" runat=server /><asp:Image runat=server ID="vimg" ImageUrl="" /><%--<a href='' onclick='window.location.href=window.location.href;' >看不清楚?</a>--%>
3<script type="text/javascript" src="js/jquery.js"></script>
4<input type=hidden id="hvimg" value="" runat=server />
5<script>
6window.onload = function(){
7 document.getElementById('<%=vimg.ClientID %>').src="verifycode.aspx?v="+document.getElementById('<%=hvimg.ClientID %>').value;
8}
9function getIMG()
10{
11
12 $.ajax({
13 type: "GET",
14 url:"getNum.aspx?timestamp="+new Date(),
15 data:"",
16 success:successfunc
17});
18
19}
20function successfunc (msg)
21{debugger
22 $("#<%=hvimg.ClientID %>").get(0).value = msg;
23 document.getElementById('<%=vimg.ClientID %>').src="verifycode.aspx?v="+document.getElementById('<%=hvimg.ClientID %>').value;
24
25}
26</script>
27<a href="javascript:void(0);" onclick="getIMG();return false;">看不清?</a>
28
29
30
31
这又几行我封装的登录验证用户控件,代码解释一下:
第一步:在服务器端随机生成一个4位数字并加密,加密串赋值给一个hidden控件,也就是代码上面的hvimg。
第二步:在window.onload的时候,将一个hidden控件的值动态拼凑在一个链接页面里作为查询字符串,这个页面verifycode.aspx?v=就是用来回发图片的。
第三步:点击看不清楚的链接,客户端发送一个ajax请求给服务器端动态请求一个加密串,然后赋值给hidden控件,并且动态将img的src改变,并成功获取新的验证码图片。
第四步:到服务器端验证,将hidden控件里的值解密后与用户输入的验证码对证。验证是否输入正确。
注:getNum.aspx为ajax请求页面,他只是返回一个加密串,期间涉及urlencode/decode问题,请大家稍微注意下
当然还有中稍微简单点的办法,基本思路是:将加密串存储在hidden内,但是不使用ajax来动态刷新验证码图片,要刷新的话就整个页面postback到服务器端。
设计稍微简单点,但是用户体验差点。
为什么要这样设计呢?
1 防止cookie被覆盖。
2 防止动态请求验证码图片的时候整体刷新页面。
3 防止被识破以及垃圾评论。
另外,我还想说一下在服务器端存储验证码,csdn就是这样(据我分析,并无真凭实据)。
大家可以试验一下:使用IE7,打开csdn页面:http://passport.csdn.net/UserLogin.aspx
在同一tab里再次打开,然后在第一个页面里输入第二次打开的验证码登录,提示登录成功,呵呵,很有意思吧!
原因为什么呢?因为他将两次打开页面的验证码都存储在了服务器端,比如某个Session下的一个hashtable,然后在登录的时候只要匹配能在hashtable里找到这个用户输入的验证码,那么就算验证码正确。
但是如果使用IE6演示,那么演示不出来.使用的IE6,为什么就不能演示成功?那为什么?因为asp.net将新开的2个IE6 视为不同的会话,这样在asp.net里会被分配成不同的asp_net_session_id cookie值,具体你不信的话可以使用fiddler跟踪查看一下cookie。既然被视为不同会话,那么Session里的hashtable都不同,当然演示不成功。
另外,新浪做得验证码特别有特色,居然还可以收听验证码,呵呵,大家可以看看这个链接,到页面尾,输入验证码的地方,有个链接,是”收听验证码“
http://blog.sina.com.cn/s/blog_57ebc672010089rx.html
但是后来被我淘腾得收听到的验证码和实际图片的验证码不一致了,哈哈。
关于验证码的东西今天就研究这么点,希望大家把自己更好的解决方案跟大家分享一下,我也只是抛砖引玉。
如果大家要那个第三个方案的源码,可以给我留言,那个我自己暂时还没有整理好,稍后会奉上。