利用图像边缘检测算子破解滑动拼图验证码

在之前用神经网络的方法搞了一把前公司的验证码以后,前同事们也快速作出响应,迅速把普通验证码换成了滑动拼图验证码,然后还说:

我觉得我可能是和验证码杠上了。

滑动验证码拼图的定位问题只是破解过程中的一个环节,我的方案主要采用 opencv 提供的函数对图片进行处理后来实现定位,在这里只提供一个思路,抛砖引玉了。

最终的实现效果可以通过 这个Demo 简单感受一下。相比之前用神经网络训练来破解普通验证码,这次的实现方式可能更具通用性,因为不用再依赖训练数据。

关于滑动验证码

演示项目所用到的滑动验证码实现相对简单,整个交互过程主要包含以下步骤:

  • 服务端将背景图片和拼图图片合并为一张图片,并记录下拼图在背景图中的x坐标,然后将拼好图片和单独的拼图图片返回给客户端
  • 客户端实现单片拼图在背景图上拖动的动画效果,并在用户完成拖动动作后,将当前拼图所处位置的坐标数据加密后返回给服务端
  • 服务端解密数据并比较客户端返回的x坐标数据并与之前保存的x坐标数据进行比较,允许小范围内的误差

实现原理

基于以上的验证码实现,本例子通过以下方式实现对验证码拼图在背景图中的定位(其他步骤较为简单,不做考虑):

  • 利用opencv库中提供的边界查找函数(cv2.findContours)提取单片拼图边缘轨迹并构造成一个二维矩阵(算子),具体代码如下:
 shape = cv2.resize(cv2.imread('shape.png'), (shape_height, shape_width))
 shape_gray = cv2.cvtColor(shape, cv2.COLOR_BGR2GRAY)

# 查找边界函数(findContours)要求先将图片转换为binary格式
 _, shape_binary = cv2.threshold(shape_gray, 127, 255, cv2.THRESH_BINARY)
 _, contours, hierarchy = cv2.findContours(shape_binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)

#所得到的contours是所有边界的列表,拼图图片中只会匹配到一个边界
contour = contours[0]

# 边界数据是一个边界点所在坐标构成的列表
operator = np.zeros((shape_height, shape_width))

# 这里需要返回一个拼图边界的矩阵用来在背景图中做匹配
for point in contour:  
  operator[point[0][0]][point[0][1]] = 1
  • 利用 高斯模糊算子(cv2.GaussianBlur)和 Canny边缘检测算子(cv2.Canny)对背景图进行处理,凸显出拼图在图片中的边缘,具体代码如下:
  img_blur = cv2.GaussianBlur(img, (3, 3), 0)
  img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)
  img_canny = cv2.Canny(img_gray, 100, 200)
  • 用拼图轨迹算子在处理后的背景图上进行 互相关操作,所得最大(小)值的位置就是拼图在背景图中的坐标

关于这个 Canny 边缘检测算子,我也小小的研究了一下,毕竟还是读了四年电工专业,没准还是以前上课学过的。推荐大家阅读这篇 Canny边缘检测的文章,已经把算法的原理说得很清楚了。

其他思考

拼图的定位只是破解滑动验证码的一个中间环节,想要破解一个好的验证码产品除了定位拼图在整个图片中的位置外,可能还有以下几个问题需要考虑:

  • 从服务端获取的背景图可能是经过加密的(需要阅读js源码获得恢复图片的算法)
  • 返回给服务端的参数一般是经过加密处理的(需要阅读js源码理解算法)
  • 服务端可能会对用户滑动行为的移动轨迹对用户进行校验(可以尝试通过selenium等工具模拟拖动行为,或者积累真实的拖动数据后学习规律并通过js源码获得构造数据的算法)

其实,我觉得对滑动轨迹的检测也是一个重点,但如果不是用第三方(比如极验之类)提供的实现,自己也比较难做好对轨迹的校验,前同事的这个就是自己实现的,不存在轨迹检测的问题,所以这里只讨论定位拼图位置这一点。

所以总体的感觉是,拼图式滑动验证码的用户体验更好,但是从安全度上看,可能还不如传统的普通验证码。

之前也考虑过是否可以尝试用深度学习的方法来达到目的,但如果用深度学习的方法来解决这个问题,将会有几个显著的问题需要面对。首先深度学习需要依赖大量的训练数据,这个条件不具备。其次,如果是以背景图上的坐标点作为输出结果来进行分类,因为点很多,训练难度会比较大。再有就是如果背景图发生变化,之前训练的模型也就失效了。综合以上因素,最终选择了边缘检测的方法来结局这个问题,突然感觉大学的专业课知识还是没有白学。不过毕竟是现炒现卖,还是比较肤浅,但是呢,至少目的还是达到了。

Show Comments