之前在WooYun知识库上写过一篇《初探验证码识别》,主要介绍了一些基本的验证码识别过程,并且给出了一些设计(改进)验证码时值得考虑的地方。正好最近没事在写Python Web玩时注册、登录等操作需要用到验证码,那么就借着这个机会自己写一个吧~

验证码

“验证码”又名“全自动区分计算机和人类的图灵测试”(英语:Completely Automated Public Turing test to tell Computers and Humans Apart,简称CAPTCHA),最早在2002年由卡内基梅隆大学的路易斯·冯·安、Manuel Blum、Nicholas J.Hopper以及IBM的John Langford所提出,是一种区分用户是计算机或人类的公共全自动程序。

利用其区可分人或计算机的特点,在Web应用中,验证码常被用于注册、登录、发布信息等处,以避免计算机程序恶意进行批量注册、暴力破解用户密码、发布大量打击信息等。

验证码的设计与实现

生成一个简单的验证码

首先,我们使用PIL (Python Imaging Library) 这个库来生成一张验证码图片。这里我们随机选择了4个字符并绘制了一张120*40的白色背景图,然后在相应位置绘制了指定字体和字号的字符。

利用上面的代码便可生成一个简单的验证码了。

5sge

但是从这个验证码字符分隔明显、前背景易区分且无其他干扰,所以它还是比较容易被识别的。因此我们需要在此基础上进一步加工这个验证码。

添加干扰

我们可以在验证码上绘制一些干扰线,ImageDraw.Draw对象的line函数可以很方便的在我们输入的两个点间绘制一条直线:

除了干扰线外我们还可以绘制一些干扰点,因试验发现干扰点较小很容易被降噪处理去掉,而较大又影响验证码的直观体验,所以这里采用了一些较小的字符来对验证码进行干扰:

这里以千分比的方式来控制干扰的强度。

随机字符位置

如果每个字符的位置固定的话,可以很容易的通过位置分隔开每个字符,所以字符位置最好是随机的。另外,通过投影或者连通区域的方法分隔字符的时候可以比较容易的将两个不粘连的字符分开,所以这里在随机范围的设定时,允许存在1/3的字符粘连:

颜色设置

在去干扰时,如果颜色上存在字符和干扰(线、字符等)的分界点的话那么所有干扰就失去了意义,所以在颜色设置方面要尽量使其存在交集,在不影响直观感受的情况下,交集越大越不容易被识别。

其他考虑

字符选择

因部分字体在没有相应辅助信息(如:对齐、拼写环境等)的条件下类似于:1、I、l,0、o、O,2、z、Z这类字符不易辨别,所以在设计验证码时一般会去掉此类字符以降低用户识别难度。

代码

完整代码如下:

 

点击数:6397

写在前

最近在写一个模拟登陆腾讯企业邮箱的东西,但是发现在POST的数据中有一个p字段的内容是一个加密的字符串。经过查看源码发现p字段的值原来是由表单中pp和ts两个字段的值拼接并进行RSA加密的结果经过base64编码后得来的,其中pp字段是用户输入的密码,而ts字段是一个时间戳(在页面源码中已经给出)。最初的想法比较偷懒,想去直接调JS代码算出个结果返回回来,但是发现调这个JS太麻烦了,还不如直接用Python代码重新实现一遍这个加密算法呢……于是乎,折腾起来吧~

关于p值的来历

实现

首先,获取RSA算法的公钥和ts值很简单,直接从页面源码中按字符串处理的方式即可提取出公钥(当然,这里上个正则秀个操作也是可以的~),而ts的值从form表单中即可获取。

 

RSA算法的实现可以直接使用第三方库rsa,将加密后结果(十六进制)做base64编码即为所求的p值。

 

如果有细心的人验证结果是否正确时可能会发现我们算出来的p值和抓包抓到的提交的p值完全不同,但是不要慌,因为RSA算法在进行加密时会进行一个随机填充(而不是填充固定值,如全0),所以由于填充的值不同,我们算出来的结果与抓包看到的p值不同是正常的,不影响正常登录。

在提交完登录表单后会得到一个“重定向”的页面,这个页面里就包含了登陆进邮箱的地址targetUrl,并通过window.location.replace的方法将窗口“重定向”到了targetUrl。

登录成功后的返回结果

好了,任务完成~

前端加密在安全上的用途

了解安全的人都知道,明文传输登录表单是件很不安全的事情,因为我们无法保证数据在传输过程中的安全性,如果表单内容被嗅探,攻击者便可轻松直接拿到我们的帐号密码。对于这个问题一个比较好的解决办法就是启用HTTPS,使用HTTPS的安全性固然很高,但是由于其在经济、性能等方面都需要花费较大的成本,所以这种方案的应用并不十分普遍,而前端加密则是一种比较常见的替代方案,它通过前端JS对敏感数据加密然后再进行传输,可以很大程度上的减少资源的消耗。

下面以登录为例,我们来看一下通过前端加密传输密码的过程。

首先在我们请求登录页面的时候,服务器会将一个公钥(PublicKey)和一段用于加密的JS代码一起发给用户,在提交表单时浏览器会先执行这段JS代码对密码进行加密,然后将加密后的密码密文连通表单其他数据一起提交到服务器。这样数据包即使在传输过程中被嗅探到,但是由于没有私钥,攻击者仍然无法拿到真正的密码。

在此基础上,为了防止重放攻击等问题,服务器通常还会在用户请求登录页面的时候给用户发送一个盐值(salt),JS在进行加密时会将盐值和密码的明文一起加密。这样,由于每次的盐值不同所以每次加密后的密文也会不同,这样可以有效的避免重放攻击。

对于上面所提到的“用于加密的JS代码”的加密算要求是公钥加密算法,这样可以有效的防止攻击者解出密码的明文,一般多为RSA算法。

最后

虽然前端通过JS加密表单敏感数据的方法新浪微博、人人网等一些网站都在使用,但是目前网上关于这种方案的相关介绍还不是很多,所以如果您有这方面的资料或者经验可以分享的欢迎随时Email我,非常感激~

点击数:4569