前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >svg-captcha的巨大漏洞发现日志

svg-captcha的巨大漏洞发现日志

作者头像
黒之染
发布2020-05-09 16:59:12
1.4K0
发布2020-05-09 16:59:12
举报

最新更新,svg-captcha即将发布3.0,修复该漏洞

详情issue:https://github.com/produck/sv...


以下原文

可能有部分nodejs开发者因为安装图形库很麻烦,都用svg-captcha来生成图形验证码

svg-captcha: https://www.npmjs.com/package...

它有不少优点,具体看官方文档。然而它最大的一个缺点就是,太容易被破解了,是我刚破解的(其实是我在2019年,第一次知道它后没多久,无意中发现破解方法的,直到现在才有空提交代码)

破解指的是,很容易被机器识别,识别率达100%,并且不需要任何机器学习有关的知识,更不需要任何图形识别库,你可能根本想象不到破解方法有多简单。

如果没有兴趣看下面那么长,可以直接看识别代码:

https://github.com/haua/svg-c...

发现流程

刚接触svg-captcha的时候就在想,它到底为防止识别做了哪些操作,然后仔细对比相同字母,发现每次生成相同字母的轮廓,不一致的地方相当多:

这样看来,即使是相同字母,它的svg path,也是完全不一样的,看来如果要破解,确实只能先把它转为位图,再做图像识别了。

然而,有一次无意中生成了两个首字母相同的验证码,看到了如下画面:

图中上下两行分别是两次生成的svg,<path位置均是第二个字母,很快意识到,两个验证码,第一个字母的svg path长度是完全一样的。

(如果不清楚svg path是啥,可以去看看svg的组成,svg可以看作是一个html标签,path是它的一个重要属性)

那既然如此,是不是可以根据svg path长度来区分它是哪个字母??

于是用js写了个脚本,遍历调用svg-captcha来生成验证码,把每个验证码中的字母path长度做个统计,一共只有26个字母+10个数字,结果如下

代码语言:javascript
复制
{
  '0': { '2382': 10819, '2580': 3769 },
  '1': { '998': 10861, '1081': 3621 },
  '2': { '2546': 11012, '2758': 3676 },
  '3': { '3878': 10913, '4201': 3599 },
  '4': { '2140': 10985, '2318': 3674 },
  '5': { '2606': 10925, '2823': 3623 },
  '6': { '2632': 10834, '2851': 3615 },
  '7': { '2042': 10956, '2212': 3636 },
  '8': { '3414': 10742, '3698': 3638 },
  '9': { '2800': 5343, '3033': 1811 },
  A: { '1840': 3618, '1844': 1825, '1993': 1885 },
  B: { '3054': 11034, '3308': 3710 },
  C: { '2198': 8820, '2199': 1536, '2200': 538, '2201': 92, '2202': 4, '2381': 3579 },
  D: { '1996': 10745, '2162': 3651 },
  E: { '2246': 10939, '2433': 3569 },
  F: { '1754': 10911, '1900': 3689 },
  G: { '3266': 10989, '3538': 3579 },
  H: { '1922': 7220, '1928': 3663, '2082': 3668 },
  I: { '986': 11125, '1068': 3628 },
  J: { '1610': 10965, '1744': 3557 },
  K: { '1706': 7291, '1709': 3534, '1848': 3656 },
  L: { '1274': 10949, '1380': 3722 },
  M: { '2279': 3560, '2282': 3675, '2321': 3666, '2466': 3649 },
  N: { '1598': 7429, '1614': 30, '1615': 379, '1616': 1312, '1617': 1728, '1618': 330, '1731': 3553 },
  O: { '2164': 10695, '2344': 3602 },
  P: { '1960': 10949, '2123': 3630 },
  Q: { '3244': 7162, '3254': 3616, '3514': 3676 },
  R: { '2104': 7284, '2107': 3580, '2279': 3682 },
  S: { '3038': 10732, '3291': 3631 },
  T: { '1478': 11016, '1601': 3623 },
  U: { '2294': 7360, '2301': 3585, '2485': 3640 },
  V: { '1298': 7251, '1311': 3589, '1406': 3666 },
  W: { '2310': 3664, '2318': 3644, '2345': 2516, '2346': 1114, '2503': 3624 },
  X: { '1598': 7382, '1604': 3635, '1731': 3614 },
  Y: { '1130': 7310, '1134': 3611, '1224': 3597 },
  Z: { '1850': 7223, '1853': 3620, '2004': 3595 },
  a: { '2332': 10944, '2526': 3711 },
  b: { '2380': 10844, '2578': 3617 },
  c: { '2498': 10924, '2706': 3720 },
  d: { '2272': 10828, '2461': 3601 },
  e: { '2501': 10802, '2709': 3558 },
  f: { '2210': 11041, '2394': 3612 },
  g: { '3160': 11074, '3423': 3708 },
  h: { '1886': 10731, '2043': 3546 },
  i: { '1360': 11038, '1473': 3605 },
  j: { '2080': 10842, '2253': 3581 },
  k: { '1634': 7341, '1637': 3601, '1770': 3664 },
  l: { '986': 10975, '1068': 3589 },
  m: { '3663': 7200, '3667': 3638, '3968': 3747 },
  n: { '2198': 10825, '2381': 3668 },
  o: { '2260': 11180, '2448': 3740 },
  p: { '2464': 10860, '2669': 3653 },
  q: { '2512': 10844, '2721': 3641 },
  r: { '1491': 11032, '1615': 3659 },
  s: { '2366': 10812, '2563': 3604 },
  t: { '1694': 10790, '1835': 3615 },
  u: { '1838': 10953, '1991': 3656 },
  v: { '1082': 10959, '1172': 3543 },
  w: { '2018': 7242, '2035': 3735, '2183': 3672 },
  x: { '1610': 7350, '1613': 3706, '1744': 3762 },
  y: { '1274': 10830, '1380': 3490 },
  z: { '1694': 11224, '1835': 3701 }
}

每个字母的值都是一个对象,这个对象的key是path长度,value是这个长度出现的次数,因为还想看看每个长度出现的概率,所以跑了几十万次,也为了防止有些长度没出现过。

可以看到,每个字母的path长度,也就那几种。这样看来,svg-captcha也太容易破解了吧!

根据以上的统计,有15个字母的path长度存在相同的情况,所以用这个方法的准确率应该不到50%

继续看看那些有相同path长度的字母,发现它们还有很大的不同,比如Il都有相同的path长度(986),但是对比一下:

左边是I,右边是l,可以看到l的最上面,要比I要高一点,虽然直接根据这个特征判断I还是l,似乎很没说服力,但是试了生成几万个Il,这个的差别都是一样的,这样的话这个特征肯定能拿来用了。

其它字母也差不多是这种细微,但可靠的特征,最终做到了100%准确识别率。

如此看来,svg-captcha也只是做到了看起来比位图验证码要难识别,实际上它更容易识别,识别方法也更原始。

建议

如果你还是想使用svg-captcha,那我建议可以多准备几套字体,写个逻辑,让它每小时换一种字体,这样生成的字母svg path会完全不一样。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 发现流程
  • 建议
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档