5. 验证码

验证码是在用户注册、登录等页面,为了防止暴力请求,所加入的一种防范措施。

每次都需要进行验证,而我我们希望只有真人在操作的时候才能通过验证。

使用验证码也是一种有效的防止crsf的方法。

5.1. 常见验证码技术

本文介绍常见的验证码技术,我们在做爬虫的时候经常需要跟这些验证码技术打交道的。

5.1.1. 传统输入式验证码

  • 主要是通过用户输入图片中的字母、数字、汉字等进行验证。

  • 原理:

    • 向服务端请求,生成随机的字符,写入会话请求,
    • 同时将随机字符生成对应图片,响应给前端;
    • 前端输入对应字符的验证码,向后台发起校验。
  • 特点:

    • 简单易操作
    • 人机交互性较好
    • 安全系数低,容易被破解
    • 采用OCR技术可轻松破解。

    验证码

5.1.2. 短信验证码

  • 通过输入有效手机号,获取短信验证码来验证用户的有效性,配合验证码的时效性,大大提高用户的真实性。

  • 移动APP较多的采用此方式

  • 特点:

    • 简单便捷
    • 有效识别真实手机号
    • 会产生显性的短信成本。
  • 需要使用第三方支持,常见的产品比如阿里大鱼

    验证码

5.1.3. 输入式的图形验证码

  • 有精美的图形,文本内容清晰可见,专注于广告展现。

  • 代表:Solve Media

  • 原理:实现方式同上,只不过是融入了广告元素在其中,无形中创收

  • 特点:与其说是验证码,倒不如说是广告位。

    验证码

5.1.4. 纯行为验证码

  • 按照要求将备选碎片直线滑动到正确的位置,或拖动滑块移动来达到验证目的。

  • 代表:极验验证码

  • 特点:

    • 操作简单,体验好
    • 单一维度,容易被逆向模拟
    • 与移动端页面切换不兼容。

    验证码 验证码

5.1.5. 图标选择与行为辅助

  • 给出一组图片,按要求点击其中一张或者多张。借用万物识别的难度阻挡机器。

  • 代表:Google新型验证码、12306验证码

  • 特点:安全性强。对于图片、图库、技术要求高。

    验证码

5.1.6. 语音验证码

  • 通过输入有效手机号,配合图形验证码,获取语音验证码
  • 有两种方式:
    • 在电脑上通过TTS语音播放给用户听到的一种方式
    • 现在比较少有的通过语音电话直接呼到用户手机,实现电话语音播报的方式。
  • 特点:语音验证,简单安全。有时会产生显性的通话成本

5.1.7. 点击式的图文验证与行为辅助

  • 通过文字提醒用户点击图中相同字的位置进行验证。

  • 代表:淘宝新型验证码、点触验证码

  • 特点:操作简单,体验良好,单一图片区域较大,破解难度大。

    验证码

5.1.8. 智能验证码

  • 通过行为特征、设备指纹、数据风控等技术,正常用户免验证,异常用户强制验证

  • 特点:简单便捷,区分人与机器、人与人、设备与设备

    验证码

5.2. 验证码自动生成

此类验证码是最容易使用的,但也最容易被破解,大致步骤:

  1. 系统内随机生成一个字符串
  2. 字符串写入session保存
  3. 字符串利用Python的画图技术,生成一张图片,并把字符串在图片上
  4. 为了减少辨识度,可以对图片进行噪点加入等
  5. 用户页面显示图片,同时要求在输入框输入图片内容
  6. 用户发送过输入的图片内容,跟服务器预存的内容进行对比

我们在下面尝试做一个简单的验证码模块:

5.2.1. 验证码视图

  • 新建viewsUtil.py,定义函数verifycode

  • 此段代码用到了PIL中的Image、ImageDraw、ImageFont模块,需要先安装Pillow(3.4.1)包, 详细文档参考 http://pillow.readthedocs.io/en/3.4.x/

    • Image表示画布对象
    • ImageDraw表示画笔对象
    • ImageFont表示字体对象,ubuntu的字体路径为“/usr/share/fonts/truetype/freefont”
    # 验证码
    def verifycode(request):
        #引入绘图模块
        from PIL import Image, ImageDraw, ImageFont
        #引入随机函数模块
        import random
    
        #定义变量,用于画面的背景色、宽、高
        bgcolor = (random.randrange(20, 100), random.randrange(
            20, 100), 255)
        width = 100
        height = 25
    
        #创建画面对象
        im = Image.new('RGB', (width, height), bgcolor)
    
        #创建画笔对象
        draw = ImageDraw.Draw(im)
    
        #调用画笔的point()函数绘制噪点
        for i in range(0, 100):
            xy = (random.randrange(0, width), random.randrange(0, height))
            fill = (random.randrange(0, 255), 255, random.randrange(0, 255))
            draw.point(xy, fill=fill)
    
        #定义验证码的备选值
        # str1 = 'ABCD123EFGHIJK456LMNOPQRS789TUVWXYZ0'
        str1 = '123456789'
    
        #随机选取4个值作为验证码
        rand_str = ''
        for i in range(0, 4):
            rand_str += str1[random.randrange(0, len(str1))]
    
        #构造字体对象
        font = ImageFont.truetype('FreeMono.ttf', 23)
        #构造字体颜色
        fontcolor = (255, random.randrange(0, 255), random.randrange(0, 255))
        #绘制4个字
        draw.text((5, 2), rand_str[0], font=font, fill=fontcolor)
        draw.text((25, 2), rand_str[1], font=font, fill=fontcolor)
        draw.text((50, 2), rand_str[2], font=font, fill=fontcolor)
        draw.text((75, 2), rand_str[3], font=font, fill=fontcolor)
        #释放画笔
        del draw
        #存入session,用于做进一步验证
        request.session['verifycode'] = rand_str
        #内存文件操作
        import io
        buf = io.BytesIO()
        #将图片保存在内存中,文件类型为png
        im.save(buf, 'png')
        #将内存中的图片数据返回给客户端,MIME类型为图片png
        return HttpResponse(buf.getvalue(), 'image/png')
    
  • 配置url:
    在urls.py中定义请求验证码视图的url

      from . import viewsUtil
    
      urlpatterns = [
          url(r'^verifycode/$', viewsUtil.verifycode),
      ]
    
  • 显示验证码

在模板中使用img标签,src指向验证码视图

<img id='verifycode' src="/verifycode/" alt="CheckCode"/>
  • 验证

    接收请求的信息,与session中的内容对比

      from django.http import HttpResponse
    
      def verifycodeValid(request):
          vc = request.POST['vc']
          if vc.upper() == request.session['verifycode']:
              return HttpResponse('ok')
          else:
              return HttpResponse('no')
    
  • 配置验证处理的url

      urlpatterns = [
          url(r'^verifycodeValid/$', views.verifycodeValid),
      ]