万恶的验证码

这两天在做一个查询域名备案信息的api接口,在工信部备案查询网站查询需要验证码。在这个问题上卡了两天,于是学习了一些关于验证码原理。

验证码机制的实现

目前主流的实现技术主要有*session*和*cookie*两种方式,而这两种方式可以说技术是一样的,区别在于将验证码字符串存储在服务器还是客户端。

前者工作流程:服务器发送验证码图片到客户端并在服务器保存验证码字符串到session,用户辨认图片并提交验证码字符串到服务器,服务器将用户提交的验证码字符串与session中保存的字符串进行比较。

后者工作流程:服务器发送验证码图片以及验证码字符串(可能会进行加密)到客户端,客户端将验证码字符串存储到本地cookie,用户辨认图片并提交验证码字符串以及cookie中所存储的字符串到服务器,服务器将用户提交的两个字符串(进行解密后)进行比较。

相对而言,存放在服务器的session更为安全,只不过消耗服务器内存,程序员除了使用模式识别辨认出验证码,没有其他办法。而对于使用cookie方式的验证码,不增加服务器内存消耗,但我们可以通过对传输数据进行分析轻易破解验证码。

实战分析

就拿工信部备案查询网站来试试咯。

点击验证码,在firebug中看到一个GET请求http://www.miitbeian.gov.cn/getVerifyCode?99,这个请求让服务器更换验证码,并返回相应验证图片。
输入验证码,点击提交。发送一个POST请求http://www.miitbeian.gov.cn/icp/publish/query/icpMemoInfo_searchExecute.action。它携带的参数:


还有cookies:


在前端还有一些js函数,保证表单和验证码输入正确才可以提交,于是用python程序模拟提交表单绕过前端的验证:

# coding:utf-8
import requests

url = 'http://www.miitbeian.gov.cn/icp/publish/query/icpMemoInfo_searchExecute.action'

cookies = dict(JSESSIONID='Bh5zXxhbXtqFfBmp3G311HR2DG9K2G1QhQTJg3v3HQQ3CBGyLw2Y!871614666',
               __jsluid='1fa6bfa2b3eb2b3dbbaf2c7283fdbabf')

header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:46.0) Gecko/20100101 Firefox/46.0'}

data = dict(siteName="", condition="", siteDomain="baidu.com", siteUrl="",
            mainLicense="", siteIp="", unitName="", mainUnitNature="",
            certType="", mainUnitCertNo="", verifyCode="77yede")
r = requests.post(url, headers=header, cookies=cookies, data=data)
print r.text

经过多次尝试发现,这个POST请求能否获得正确响应的关键就在verifyCodeJSESSIONID。在后端,这两个参数应该是绑定在一起的,而getVerifyCode这个GET请求会刷新verifyCode,并且一旦提交正确的POST请求,此次绑定就失效了,再次提交会被拒绝服务。只有用getVerifyCode请求刷新后,才能再次查询。
用流程图表示就是:

总结

毕竟是工信部的网站,我并没有找到什么可以绕过验证的方法(除了机器识别验证码......)。那就拿它当作一个正面列子,一个好的验证码系统就是:

  • 1.客户端发起一个请求
  • 2.服务端响应并创建一个新的SessionID同时生成一个随机验证码。
  • 3.服务端将验证码和SessionID一并返回给客户端
  • 4.客户端提交验证码连同SessionID给服务端
  • 5.服务端验证验证码同时销毁当前会话,返回给客户端结果
绕过验证码

要想优雅地绕过验证码,大概可以从:客户端问题、服务端问题、验证码本身,验证码流程设计这几个方面入手。这次遇到的验证码貌似还没有这些问题,所以以后遇到了再说咯。
这方面的信息,可以参考:验证码安全问题汇总


reference:
小议如何绕开验证码(原理)
验证码安全问题汇总
提供一个域名备案查询的 API

Comments
Write a Comment
  • 谢谢博主,有所收获!我研究过一段时间Discuz的验证码,无果,博主有兴趣的话去试试吧!