专栏名称: 唤之
目录
相关文章推荐
程序员的那些事  ·  李彦宏自曝开源真相:从骂“智商税”到送出“史 ... ·  2 天前  
OSC开源社区  ·  大模型训练中的开源数据和算法:机遇及挑战 ·  2 天前  
程序员的那些事  ·  if微信+DeepSeek=王炸,百度+De ... ·  4 天前  
51好读  ›  专栏  ›  唤之

15 分钟用 ML 破解一个验证码系统

唤之  · 掘金  · 程序员  · 2018-01-30 06:12

正文

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

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

1*4q8hCiIh1amCf2e9fJBQGg

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

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

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

1*3qc-gFTRWmleRXomopMAUQ

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

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

挑战

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

1*WDu1xgEEQuP3tqgC-cPmzA

好了,所以 验证码 似乎是四个字母。在 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',
		);

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

计时:2 分钟

工具

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

Python 3

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

OpenCV

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

Keras

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

TensorFlow

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

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

创造我们的数据集

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

1*dC78m_XpcZmuaV_GlnAh5g

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

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

1*leaOO0EYbgKVl7MEhflIFA

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

计时:5 分钟

简化问题

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

1*4ScTIDYJ6rPCAtopRulzOg

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

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

1*4ScTIDYJ6rPCAtopRulzOg

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

1*yyfjNSCKt8IvY7JqANnOZg

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

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

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

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

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

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

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

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

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

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

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

计时:10 分钟

构建并训练神经系统

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

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

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







请到「今天看啥」查看全文