15分钟实战机器学习:验证码(CAPTCHA)识别

让我们使用机器学习(machine learning)来绕过世界上最流行的Wordpress验证码插件!

每个人都不喜欢验证码(CAPTCHA) - 那些令人讨厌的图像中包含了你必须正确输入的文本,只有输入成功后才能访问网站。 验证码旨在通过验证你是一个真实的人来防止机器(蠕虫)自动填写表格。 但随着深度学习和计算机视觉的兴起,现在很容易使用机器来自动识别验证码了。

我一直在阅读Adrian Rosebrock的书计算机视觉:基于Python的深度学习。在这本书中,Adrian利用机器学习技术绕过了E-ZPass New York网站的验证码:

e-zpass break

Adrian访问不了用来生成验证码图像的应用程序源代码。因此为了实现上述系统,他只能下载数百个样本图像,人工识别答案,然后作为训练数据来训练他的机器学习系统。

但是如果我们想要破解的是一个开源的验证码系统,是不是有更好的办法?

我在WordPress.org插件重心搜索“captcha”。 排在最前面的被称为“Really Simple CAPTCHA”,有总计超过100万次的安装:

really simple captcha

最重要的是,它自带源码! 由于我们有了用来生成验证码的源代码,所以这应该很容易破解。 为了让事情更有挑战性,让我们给自己一个时间限制。 能在15分钟内绕过这个验证码系统吗? 让我们试试!

重要声明: 这绝不是对“Really Simple CAPTCHA”插件或其作者的批评。 插件作者自己说,不能继续保证这个系统所生成验证码的安全性,建议你使用别的替代品。 这只是一个有趣、快速的技术挑战。 但是,如果你是上述100多万用户之一,也许你应该考虑切换到其他的产品:)

挑战

为了组织一个攻击计划,让我们看看Really Simple CAPTCHA生成的验证码图像。 在演示网站上,我们看到:
rsc demo

好,验证码图片看起来包含四个字母。 让我们在其PHP源代码中加以验证:

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',
);

的确如此!它使用四种不同字体的随机组合来生成4个字母的验证码。 而且我们可以看到它从不在代码中使用“O”或“I”来避免用户混淆。 现在还剩下32个可能的字母和数字需要进行识别。 没问题!

已用时:2分钟

工具集

在继续之前,让我们来看一下解决这个问题要用到的工具:

  • Python 3Python是一款有趣的编程语言,有大量用于机器学习和计算机视觉的优秀的库。
  • OpenCVOpenCV是一个流行的计算机视觉和图像处理框架。 我们将使用OpenCV来处理验证码图像。 它有一个PythonAPI,所以我们可以直接从在Python中使用它。
  • KerasKeras是一个用Python开发的深度学习框架。 Keras的存在简化了深度神经网络的定义、训练和使用,通常你只需要编写少量的代码即可获得一个网络。
  • TensorFlowTensorFlowGoogle的机器学习库。 我们使用Keras进行开发,但Keras实际上本身并没有实现神经网络的逻辑。 相反,它使用后端的TensorFlow库来完成繁重的工作。

好,回到挑战!

创建数据集

要开发任何机器学习系统,都需要训练数据。 为了识别验证码,我们需要如下的训练数据:

train data

由于我们有WordPress插件的源代码,因此可以对其进行修改,生成10,000张验证码图像以及对应的正确答案。

只需要几分钟的代码编写,添加一个简单的’for’循环,我就得到了一个文件夹与训练数据 - 10,000PNG文件,文件名就是正确的答案:

data files

这是我不会给你示例代码的唯一部分。 我们写这篇文章的目的是为了学习交流,我不希望你真的跑去给那些WordPress网站发送垃圾邮件。 不过,我会给你最后生成的10,000张图片,以便你可以重现我的结果。

已用时:5分钟

简化问题

现在有了训练数据,我们可以直接使用它来训练一个神经网络:

neural network

有了足够的训练数据,这种方法可能行的通 - 但我们可以让问题变得更容易解决。 问题越简单,训练数据越少,我们需要的计算能力就越少。 毕竟我们只有15分钟!

幸运的是,验证码图像总是由四个字母组成。 如果我们能够以某种方式切分图像,使每个字母位于单独的图像,那么我们只需要训练神经网络一次识别一个字母:

ann2

我没有时间浏览10,000个训练图像,并在Photoshop中手动将它们分割成单独的图像。 这可能需要几天的时间,而我只剩下10分钟了。 而且我们不能将图像分成四个相同大小的块,因为验证码会将这些字母随机放置在不同的水平位置:

random character

幸运的是,我们仍然可以自动执行此操作。 在图像处理中,我们经常需要检测具有相同颜色的像素团(blob)。 这些像素团周围的边界被称为轮廓 (contour)。 OpenCV有一个内置的findContours()函数,可以用来检测这些连续的区域。

我们将从一个原始的验证码图像开始:

captcha image

然后,将图像从灰度转换为黑白(阈值化:thresholding ),这样就很容易找到连续的区域:

thresholding

接下来,我们将使用OpenCVfindContours()函数来检测包含相同颜色像素团的单独部分:

find countours

然后,将每个区域保存为一个单独的图像文件就很简单了。 而且由于我们知道每个图像应该包含从左到右的四个字母,所以可以使用这些知识来标注字母。 只需要按顺序用适当的字母名称保存每个图像字母即可。

但是等等 - 我发现一个问题! 有时候验证码有这样的重叠字母:

overlap

这意味着我们最终提取到的是两个字母拼在一起的区域:

overlap2

如果我们不处理这个问题,最终只能得到质量不高的训练数据。 我们需要解决这个问题,以免不小心让机器把这两个重叠的字母识别为一个。

这里有个简单的技巧,就是说,如果一个轮廓区域的宽度比高度大很多,就意味着很可能把两个字母挤在一起了。 在这种情况下,我们可以将连体字母拆分成两半,并将其视为两个单独的字母:

split overlap

现在有了提取单个字母的方法,让我们在所有的验证码图像上运行这个代码。 目标是收集到每个字母的不同变化形态。 我们可以将每个字母保存在单独的文件夹中,以保持良好的数据样本组织。

下面是我提取所有字母后,W文件夹的样子:

w folder

已用时:10分钟

建立和培养神经网络

由于只需要识别单个字母/数字的图像,因此我们不需要一个特别复杂的神经网络。 识别字母比识别阿猫阿狗这样的复杂图像要容易得多。

我们使用具有两个卷积层(convolutional layer)和两个全连接层(fully-connected layer)的简单卷积神经网络:

network

如果你想知道卷积神经网络的工作原理,以及为什么它特别适用于图像识别任务,请查看Adrian的或我以前的文章

Keras中定义这个神经网络只需要几行代码:

# 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"])

现在可以训练了!

# 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. 狂欢吧!

下面的动图展示了我们的模型是如何识别验证码的:

how it works

或者,命令行输出:

command line

试试看!

如果你想亲手尝试一下,可以在这里获取源代码 。 它包括10,000个示例图像和本文中每个步骤的所有代码。 在运行之前,请先查看其中包含的README.md文件。

但是,如果想理解每一行代码,我强烈建议你找这本书(计算机视觉:基于Python的深度学习)来看看。 它深入很多的细节,并且有大量的详细示例。 这是迄今为止我所见过的唯一的一本书,即讲解了机器学习的运行原理,也讲解了如何使用机器学习技术来解决现实中的难题 。 试一试!

如果你喜欢这篇文章,别忘了关注我!

原文:How to break a CAPTCHA system in 15 minutes with Machine Learning

没有更多推荐了,返回首页