让我们使用机器学习(machine learning
)来绕过世界上最流行的Wordpress
验证码插件!
每个人都不喜欢验证码(CAPTCHA
) - 那些令人讨厌的图像中包含了你必须正确输入的文本,只有输入成功后才能访问网站。 验证码旨在通过验证你是一个真实的人来防止机器(蠕虫)自动填写表格。 但随着深度学习和计算机视觉的兴起,现在很容易使用机器来自动识别验证码了。
我一直在阅读Adrian Rosebrock
的书计算机视觉:基于Python的深度学习。在这本书中,Adrian
利用机器学习技术绕过了E-ZPass New York
网站的验证码:
Adrian
访问不了用来生成验证码图像的应用程序源代码。因此为了实现上述系统,他只能下载数百个样本图像,人工识别答案,然后作为训练数据来训练他的机器学习系统。
但是如果我们想要破解的是一个开源的验证码系统,是不是有更好的办法?
我在插件重心搜索“captcha”。 排在最前面的被称为“Really Simple CAPTCHA”,有总计超过
100
万次的安装:
最重要的是,它自带源码! 由于我们有了用来生成验证码的源代码,所以这应该很容易破解。 为了让事情更有挑战性,让我们给自己一个时间限制。 能在15
分钟内绕过这个验证码系统吗? 让我们试试!
重要声明: 这绝不是对“Really Simple CAPTCHA”插件或其作者的批评。 插件作者自己说,不能继续保证这个系统所生成验证码的安全性,建议你使用别的替代品。 这只是一个有趣、快速的技术挑战。 但是,如果你是上述100
多万用户之一,也许你应该考虑切换到其他的产品:)
挑战
为了组织一个攻击计划,让我们看看Really Simple CAPTCHA
生成的验证码图像。 在演示网站上,我们看到:
好,验证码图片看起来包含四个字母。 让我们在其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 3:Python
是一款有趣的编程语言,有大量用于机器学习和计算机视觉的优秀的库。OpenCV:OpenCV
是一个流行的计算机视觉和图像处理框架。 我们将使用OpenCV
来处理验证码图像。 它有一个Python
的API
,所以我们可以直接从在Python
中使用它。Keras:Keras
是一个用Python
开发的深度学习框架。Keras
的存在简化了深度神经网络的定义、训练和使用,通常你只需要编写少量的代码即可获得一个网络。TensorFlow:TensorFlow
是Google
的机器学习库。 我们使用Keras
进行开发,但Keras
实际上本身并没有实现神经网络的逻辑。 相反,它使用后端的TensorFlow
库来完成繁重的工作。
好,回到挑战!
创建数据集
要开发任何机器学习系统,都需要训练数据。 为了识别验证码,我们需要如下的训练数据:
由于我们有WordPress
插件的源代码,因此可以对其进行修改,生成10,000
张验证码图像以及对应的正确答案。
只需要几分钟的代码编写,添加一个简单的’for’循环,我就得到了一个文件夹与训练数据 -10,000
个PNG
文件,文件名就是正确的答案:
这是我不会给你示例代码的唯一部分。 我们写这篇文章的目的是为了学习交流,我不希望你真的跑去给那些WordPress
网站发送垃圾邮件。 不过,我会给你最后生成的10,000
张图片,以便你可以重现我的结果。
已用时:5
分钟
简化问题
现在有了训练数据,我们可以直接使用它来训练一个神经网络:
有了足够的训练数据,这种方法可能行的通 - 但我们可以让问题变得更容易解决。 问题越简单,训练数据越少,我们需要的计算能力就越少。 毕竟我们只有15
分钟!
幸运的是,验证码图像总是由四个字母组成。 如果我们能够以某种方式切分图像,使每个字母位于单独的图像,那么我们只需要训练神经网络一次识别一个字母:
我没有时间浏览10,000
个训练图像,并在Photoshop
中手动将它们分割成单独的图像。 这可能需要几天的时间,而我只剩下10
分钟了。 而且我们不能将图像分成四个相同大小的块,因为验证码会将这些字母随机放置在不同的水平位置:
幸运的是,我们仍然可以自动执行此操作。 在图像处理中,我们经常需要检测具有相同颜色的像素团(blob
)。 这些像素团周围的边界被称为轮廓 (contour
)。OpenCV
有一个内置的findContours()
函数,可以用来检测这些连续的区域。
我们将从一个原始的验证码图像开始:
然后,将图像从灰度转换为黑白(阈值化:thresholding
),这样就很容易找到连续的区域:
接下来,我们将使用OpenCV
的findContours()
函数来检测包含相同颜色像素团的单独部分:
然后,将每个区域保存为一个单独的图像文件就很简单了。 而且由于我们知道每个图像应该包含从左到右的四个字母,所以可以使用这些知识来标注字母。 只需要按顺序用适当的字母名称保存每个图像字母即可。
但是等等 - 我发现一个问题! 有时候验证码有这样的重叠字母:
这意味着我们最终提取到的是两个字母拼在一起的区域:
如果我们不处理这个问题,最终只能得到质量不高的训练数据。 我们需要解决这个问题,以免不小心让机器把这两个重叠的字母识别为一个。
这里有个简单的技巧,就是说,如果一个轮廓区域的宽度比高度大很多,就意味着很可能把两个字母挤在一起了。 在这种情况下,我们可以将连体字母拆分成两半,并将其视为两个单独的字母:
现在有了提取单个字母的方法,让我们在所有的验证码图像上运行这个代码。 目标是收集到每个字母的不同变化形态。 我们可以将每个字母保存在单独的文件夹中,以保持良好的数据样本组织。
下面是我提取所有字母后,W
文件夹的样子:
已用时:10
分钟
建立和培养神经网络
由于只需要识别单个字母/数字的图像,因此我们不需要一个特别复杂的神经网络。 识别字母比识别阿猫阿狗这样的复杂图像要容易得多。
我们使用具有两个卷积层(convolutional layer
)和两个全连接层(fully-connected layer
)的简单卷积神经网络:
如果你想知道卷积神经网络的工作原理,以及为什么它特别适用于图像识别任务,请查看Adrian的书或我以前的文章 。
在Keras
中定义这个神经网络只需要几行代码:
# Build the neural network!model = Sequential()# First convolutional layer with max poolingmodel.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 poolingmodel.add(Conv2D(50, (5, 5), padding="same", activation="relu"))model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))# Hidden layer with 500 nodesmodel.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 pile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
现在可以训练了!
# Train the neural networkmodel.fit(X_train, Y_train, validation_data=(X_test, Y_test), batch_size=32, epochs=10, verbose=1)
经过10
轮训练后,我们达到了将近100%
的准确度。现在,我们应该能够自动绕过这个验证码了! 我们做到了!
已用时:15分钟(喔!)
使用训练好的模型来识别验证码
现在我们有了一个训练好的神经网络,用它来识别验证码非常简单:
从使用此WordPress
插件的网站抓取真实的验证码图像。使用我们用来创建训练数据集的相同方法,将验证码图像分解为四个独立的字母图像。让神经网络对每个字母图像做一个单独的预测。使用四个预测字母作为验证码的答案。狂欢吧!
下面的动图展示了我们的模型是如何识别验证码的:
或者,命令行输出:
试试看!
如果你想亲手尝试一下,可以在这里获取源代码 。 它包括10,000
个示例图像和本文中每个步骤的所有代码。 在运行之前,请先查看其中包含的README.md
文件。
但是,如果想理解每一行代码,我强烈建议你找这本书(计算机视觉:基于Python的深度学习)来看看。 它深入很多的细节,并且有大量的详细示例。 这是迄今为止我所见过的唯一的一本书,即讲解了机器学习的运行原理,也讲解了如何使用机器学习技术来解决现实中的难题 。 试一试!
如果你喜欢这篇文章,别忘了关注我!
原文:How to break a CAPTCHA system in 15 minutes with Machine Learning