人人都恨验证码——那些恼人的图片,显示着你在登陆某网站前得输入的文本。设计验证码的目的是,通过验证你是真实的人来避免电脑自动填充表格。但是随着深度学习和计算机视觉的兴起,现在验证码常常易被攻破。

我拜读了 Adrian Rosebrock 写的《Deep Learning for Computer Vision with Python》。在书中,Adrian 描述了他是怎样用机器学习绕过纽约 E-ZPass 网站上的验证码:

Adrian 无法接触到该应用生成验证码的源代码。为了攻破该系统,他不得不下载数百张示例图片,并手动处理它们来训练他自己的系统。

但是如果我们想攻破的是一个开源验证码系统,我们确实能接触到源代码该怎么办呢?

我访问了 WordPress.org 的插件频道,并搜索了“验证码”。第一条搜索结果是 Really Simple CAPTCHA,并且有超过一百万次的活跃安装:

最好的一点是,它是开源的!既然我们已经有了生成验证码的源代码,那它应该挺容易被攻破的。为了让这件事更有挑战性,让我们给自己规定个时限吧。我们能在 15 分钟内完全攻破这个验证码系统吗?来试试吧!

重要说明:这绝不是对 Really Simple CAPTCHA 插件或对其作者的批评。该插件作者自己说它已经不再安全了,建议使用其他插件。这仅仅是一次好玩又迅速的技术挑战。但是如果你是那剩余的一百多万用户之一,也许你应该改用其他插件 :)

挑战

为了构思一个攻击计划,来看看 Really Simple CAPTCHA 会生成什么样的图片。在示例网站上,我们看到了以下图片:

好了,所以验证码似乎是四个字母。在 PHP 源代码中对其进行验证:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function __construct() {
/* Characters available in images */
$this->chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
 
/* Length of a word in an image */
$this->char_length = 4;
 
/* Array of fonts. Randomly picked up per character */
$this->fonts = array(
dirname( __FILE__ ) . '/gentium/GenBkBasR.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasI.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasBI.ttf',
dirname( __FILE__ ) . '/gentium/GenBkBasB.ttf',
);

没错,它用四种不同字体的随机组合来生成四个字母的验证码。并且可以看到,它在代码中从未使用 O 或者 I,以此避免用户混淆。总共有 32 个可能的字母和数字需要我们识别。没问题!

计时:2 分钟

工具

在进行下一步前,提一下我们要用来解决问题的工具:

Python 3 

Python 是一种有趣的编程语言,它有大量的机器学习和计算机视觉库。

OpenCV 

OpenCV 是一种流行的计算机视觉和图片处理框架。我们要使用 OpenCV 来处理验证码图片。由于它有 Python API,所以我们可以直接从 Python 中使用它。

Keras

Keras 是用 Python 编写的深度学习框架。它使得定义、训练和用最少的代码使用深度神经网络容易实现。

TensorFlow 

TensorFlow 是 Google 的机器学习库。我们会用 Keras 编程,但是 Keras 并没有真正实现神经网络的逻辑本身,而是在幕后使用 Google 的 TensorFlow 库来挑起重担。

好了,回到我们的挑战吧!

创造我们的数据集

为了训练任何机器学习系统,我们需要训练数据。为了攻破一个验证码系统,我们想要像这样的训练数据:

鉴于我们有 WordPress 插件的源代码,我们可以调整它,一起保存 10,000 张验证码图片及分别对应的答案。

经过几分钟对代码的攻击,并添加了一个简单的 for 循环之后,我有了一个训练数据的文件夹——10,000 个 PNG 文件,文件名为对应的正确答案:

这是唯一一个我不会给你示例代码的部分。我们做这个是为了教育,我不希望你们真去黑 WordPress 网站。但是,我最后会给你生成的这10,000 张图片,这样你就能重复我的结果了。

计时:5 分钟

简化问题

既然有了训练数据,就可以直接用它来训练神经网络了:

有了足够的训练数据,这个方法可能会有用——但是我们可以使问题更简化来解决。问题越简单,要解决它需要的训练数据就越少,需要的计算能力也越低。毕竟我们只有 15 分钟!

幸运的是,验证码图片总是由仅仅四个字母组成。如果我们能想办法把图片分开,使得每个字母都在单独的图片中,这样我们只需要训练神经网络一次识别一个字母:

我没有时间去浏览 10,000 张训练图片并在 Photoshop 中手动把它们拆分开。这得花掉好几天的时间,而我只剩下 10 分钟了。我们还不能把图片分成相等大小的四块,因为该验证码插件把字母随机摆放在不同的水平位置上以防止这一做法:

幸运的是,我们仍然可以自动处理。在图像处理中,常常需要检测有相同颜色的像素块。这些连续像素块周围的界限被称为轮廓。OpenCV 中有一个 LndContours() 函数,可以被用来检测这些连续区域。

所以我们用一个未经处理的验证码图片开始:

接下来把该图片转换成纯黑白(这叫做 thresholding),这样容易找到连续区域:

接着,使用 OpenCV 的 LndContours() 函数来检测该图片中包含相同颜色像素块的不同部分:

接下来就是简单地把每个区域存成不同的图片文件。鉴于我们知道每张图片都应该包含从左到右的四个字母,我们可以利用这一点在保存的同时给字母标记。只要我们是按顺序保存的,我们就应该能保存好每个图片字母及其对应的字母名。

但是等等——我看到一个问题!有时验证码中有像这样重叠的字母:

这意味着我们会把两个字母分离成一个区域:

如果不处理这个问题,会创造出糟糕的训练数据。我们得解决这个问题,这样就不会意外地教机器把两个重叠的字母识别成一个字母了。

一个简单的方法是,如果一个轮廓区域比它的高度更宽,这意味着很可能有两个字母重叠在一起了。在这种情况下,我们可以把重叠的字母从中间拆分成两个,并将其看作两个不同的字母:

既然我们找到拆分出单个字母的方法了,就对所有验证码图片进行该操作。目标是收集每个字母的不同变体。我们可以将每个字母保存在各自对应的文件夹中,以保持条理。

在我分离出所有字母后,我的 W 文件夹长这样:

计时:10 分钟

构建并训练神经系统

由于我们只需要识别单个字母和数字的图片,我们不需要非常复杂的神经网络结构。识别字母要比识别像猫狗这样复杂的图片容易得多。

我们要使用简单的卷积神经网络结构,有两层卷积层以及两层完全连接层:

如果你想要了解更多神经网络的工作,以及为什么它们是图片识别的理想工具,请参考 Adrian 的书或者我之前的文章

定义该神经网络结构,只需要使用 Keras 的几行代码:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Build the neural network!
model = Sequential()
 
# First convolutional layer with max pooling
model.add(Conv2D(20, (5, 5), padding="same", input_shape=(20, 20, 1), activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
 
# Second convolutional layer with max pooling
model.add(Conv2D(50, (5, 5), padding="same", activation="relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
 
# Hidden layer with 500 nodes
model.add(Flatten())
model.add(Dense(500, activation="relu"))
 
# Output layer with 32 nodes (one for each possible letter/number we predict)
model.add(Dense(32, activation="softmax"))
 
# Ask Keras to build the TensorFlow model behind the scenes
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])

现在我们可以训练它了!

 
 
1
2
# Train the neural network
model.fit(X_train, Y_train, validation_data=(X_test, Y_test), batch_size=32, epochs=10, verbose=1)

在 10 通过了训练数据集后,我们达到了几乎 100% 的正确率。此时,我们应该能随时自动绕过这个验证码了!我们成功了!

计时:15 分钟(好险!)

使用训练后的模型来处理验证码

既然有了一个训练后的神经网络,利用它来攻破真实的验证码要很容易了:

  • 1.从一个使用 WordPress 插件的网站上下载一张验证码图片。
  • 2.使用文章中生成训练数据集的方法,把该验证码图片拆分成四张字母图片。
  • 3.用神经网络对每张字母图片分别作预测。
  • 4.用四个预测字母作为验证码的答案。
  • 5.狂欢!厦门叉车租赁公司

在破解验证码时,我们的模型看起来是这样:

或者从命令来看:

来试试吧!

如果你想自己试试,你可以从这里找到代码( http://t.cn/R8yFJiN )。它包含 10,000 张示例图片和文章中每一步的所有代码。参考文件 README.md 中的运行指导。

但是如果你想了解每一行代码都做了什么,我强烈建议你看看《 Deep Learning for Computer Vision with Python。该书覆盖了更多的细节,而且有大量的详细示例。这本书是我目前见过的唯一一本既包含了运行原理,又包含了如何在现实生活中用其来解决复杂问题的书。去看看吧!

15 分钟用 ML 破解一个验证码系统的更多相关文章

  1. 我不是机器人:谷歌最新版验证码系统ReCaptcha破解已开源

    选自 Github 作者:George Hughey 机器之心编译 每个人都讨厌验证码,这些恼人的图片中包含你必须输入的字符,我们只有正确地填写才能继续访问网站.验证码旨在确认访问者是人还是程序,并防 ...

  2. 【干货】零基础30分钟让你拥有一个完整属于自己的短视频APP系统

      目录 一.附言: 1 二.购买域名和购买服务器: 2 三.搭建服务器环境: 5 四.配置APP前端部分: 8 1.工具以及文件准备: 9 2.配置后端接口地址 11 3.配置APP启动图和启动图标 ...

  3. top,它们的意思分别是1分钟、5分钟、15分钟内系统的平均负荷。

    理解Linux系统负荷   作者: 阮一峰 日期: 2011年7月31日   一.查看系统负荷 如果你的电脑很慢,你或许想查看一下,它的工作量是否太大了. 在Linux系统中,我们一般使用uptime ...

  4. 获取当前时间UTC时间的下一个15分钟时间点

    ZonedDateTime zdt = ZonedDateTime.now(ZoneOffset.UTC); int now15Minute = zdt.getMinute() / P15MINUTE ...

  5. [ASP.NET MVC2 系列] ASP.Net MVC教程之《在15分钟内用ASP.Net MVC创建一个电影数据库应用程序》

    [ASP.NET MVC2 系列]      [ASP.NET MVC2 系列] ASP.Net MVC教程之<在15分钟内用ASP.Net MVC创建一个电影数据库应用程序>       ...

  6. Flask学习之旅--用 Python + Flask 制作一个简单的验证码系统

    一.写在前面 现在无论大大小小的网站,基本上都会使用验证码,登录的时候要验证,下载的时候要验证,而使用的验证码也从那些简简单单的字符图形验证码“进化”成了需要进行图文识别的验证码.需要拖动滑块的滑动验 ...

  7. 使用深度学习来破解 captcha 验证码(转)

    使用深度学习来破解 captcha 验证码 本项目会通过 Keras 搭建一个深度卷积神经网络来识别 captcha 验证码,建议使用显卡来运行该项目. 下面的可视化代码都是在 jupyter not ...

  8. 15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码)

    15分钟带你了解前端工程师必知的javascript设计模式(附详细思维导图和源码) 前言 设计模式是一个程序员进阶高级的必备技巧,也是评判一个工程师工作经验和能力的试金石.设计模式是程序员多年工作经 ...

  9. 15分钟学会使用Git和远程代码库

    git是个了不起但却复杂的源代码管理系统.它能支持复杂的任务,却因此经常被认为太过复杂而不适用于简单的日常工作.让我们诚实一记吧:Git是复杂的,我们不要装作它不是.但我仍然会试图教会你用(我的)基本 ...

随机推荐

  1. UGA,PGA

    tom认为UGA不包含 sort工作区,所以下面的图都是错误的 The UGA is, in effect, your session’s state. It is memory that your ...

  2. [iOS]一些第三方库

    BHInfiniteScrollView 地址 https://github.com/qylibohao/BHInfiniteScrollView 功能 图片轮播 TZImagePickerContr ...

  3. vue组件中,iview的modal组件爬坑--modal的显示与否应该是使用v-show

    这是我第一次写博客,主要是记录下自己解决问题的过程和知识的总结,如有不对的地方欢迎指出来! 需求:点击btn,弹出modal显示图表(以折现图为例) 这应该是很基本的需求也是很容易实现的,代码和效果如 ...

  4. PHP读取excel表格,和导出表格

    读取表格 public function excel(){ import("Common.Vendor.Excel.PHPExcel"); $filename="./fi ...

  5. 大数据学习--day17(Map--HashMap--TreeMap、红黑树)

    Map--HashMap--TreeMap--红黑树 Map:三种遍历方式 HashMap:拉链法.用哈希函数计算出int值. 用桶的思想去存储元素.桶里的元素用链表串起来,之后长了的话转红黑树. T ...

  6. docker入门——安装(CentOS)、镜像、容器

    Docker简介 什么是docker 官方解释: Docker is the company driving the container movement and the only container ...

  7. 【npm 指令】 (不定时持续更新)

    查看webpack所有的版本及更多信息 npm info webpack 查看webpack所有的版本 npm info webpack versions 查看webpack最新的版本 npm vie ...

  8. 常见的Content-Type类型

    Content-Type说明 MediaType,即是Internet Media Type,互联网媒体类型:也叫做MIME类型, 在Http协议消息头中,使用Content-Type来表示具体请求中 ...

  9. 20155204 2016-2017-2 《Java程序设计》第4周学习总结

    20155204 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 继承是类与类之间的联系,接口是方法与类之间的联系,多态就是指利用接口和继承来派生许多类. 有 ...

  10. 【转】 线段树完全版 ~by NotOnlySuccess

    载自:NotOnlySuccess的博客 [完全版]线段树 很早前写的那篇线段树专辑至今一直是本博客阅读点击量最大的一片文章,当时觉得挺自豪的,还去pku打广告,但是现在我自己都不太好意思去看那篇文章 ...