分类 业务安全 下的文章

前端动态变化对抗Selenium类自动化工具思路探索

0x01 前言

这不是一篇安全技术文章,如果你关注业务安全,羊毛党对抗,爬虫对抗,可以慢慢观看。

在业务安全领域最大的困扰是来自各种各样的自动化工具的薅羊毛行为,羊毛党所使用的自动化武器五花八门,其中模拟更像真人的,使用比较多的是基于Selenium库实现的操作各种真实浏览器模拟的操作。Selenium库提供的webdriver,支持主流的浏览器如:chrome,firefox, ie,opera,phantomjs,safari 也支持浏览器的headless模式,更多的介绍可以看文章:https://www.cnblogs.com/zhaof/p/6953241.html

0x02 自动化工具的demo

羊毛党在薅羊毛前就会准备好自动化工具,如抢票活动的薅羊毛,他们需要自动化工具能够完成打开浏览器,打开登录网页,填充账号密码信息,点击完成登录,打开活动页面,点击抢票等一系列操作。

下面给出的一个demo是使用Selenium +Chrome 浏览器模拟的对测试网站demo. testfire.net进行自动化登录(或撞库,爆破密码)的代码。

201812051544014778183194.png

上面的代码中首先使用Selenium中的webdriver 打开本地的Chrome 浏览器,然后利用其提供的API 接口,直接打开登录地址。要完成填充账号,密码的信息,需要先找到输入位置。Selenium 提供了多种查找元素的接口,可以通过id ,name ,xpath ,css selector ,甚至通过文本来定位。当找到操作元素的位置后就可以对其进行相应的点击,输入,拖动等动作了。

0x03 GET一些知识背景

元素的定位应该是自动化测试的核心,要想操作一个对象,首先应该识别这个对象。 一个对象就是一个人一样,他会有各种的特征(属性),如比我们可以通过一个人的身份证号,姓名,或者他住在哪个街道、楼层、门牌找到这个人。

那么一个对象也有类似的属性,我们可以通过这个属性找到这对象。 webdriver 提供了一系列的对象定位方法,常用的有以下几种:

  • id
  • name
  • name
  • class name
  • link text
  • partial link text
  • tag name
  • xpath
  • css selector

我们拿百度搜索的页面来做例子,分别使用不同的定位方法下python的调用参数如下:

百度搜索输入框的input标签如下:

<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">

哪么使用Selenium的自动化工具实现定位元素的方式有如下:

1.通过id定位

u = dr.find_element_by_id('kw')

2.通过name定位

u = dr.find_element_by_name('wd')

3.通过class name定位

s = dr.find_element_by_class_name('s_ipt')

4.通过xpath定位

s = dr.find_element_by_xpath('//*[@id="kw"]')

5.通过css selector定位

s = dr.browser.find_element_by_css_selector('#kw')

以上5种是使用Selenium编写自动化工具定位元素时使用的最多的方式,(其他的就不做列举了)可以看出自动化工具查找元素时对于这个输入标签的id,name, class等值的依赖是十分的强的。

我们再看看浏览器上javascript提供的查找元素的接口

201812051544016163471346.png

同样是使用了id, name, class的值进行的元素查找.

0x04 思考对抗思路

这种类型的利用驱动程序调用浏览器模拟人为操作的攻击方式通过传统的js防爬,UA 黑名单等方式是比较难防护的,因为这种攻击是真实浏览器发起的操作。回想到上面,这类攻击实现是通过webdriver 驱动浏览器进行的自动化操作,它的原理是通过注入自己的js 脚本到浏览器的每一个页面,通过js 完成页面的自动化点击,输入操作。哪么第一个思路就是去检测webdriver注入的js,这是目前一些防自动化攻击的厂商产品思路。防护产品A给每一个页面注入检测js ,通过检测webdriver 注入js 时带来的函数名,变量名等,实现自动化工具识别。这个检测思路也确实有效,但是这就像是杀软对抗病毒一样,通过特征库进行查杀。一定特征函数名,特征变量名等发生了变化如攻击者重写了webdriver 的驱动程序,修改了特征,哪防护产品就毫无办法了。这也是杀软一直以来面临的困扰。。。

变化一下思路,我们知道通过js 操作页面,同样需要先查找到元素。通过document. find ElenentBy**,最常见的是通过ID ,name 来查找元素。如果让 webdriver 的js注入过程是成功的,如果动态变化了标签的id, name, class name值,那么注入的JS脚本的find 定位元素过程是失败的,同样也能起到防护自动化工具的作用。

新的思路1:标签属性动态变化,干扰js 查找元素过程

对于防护产品而言,第一阶段保留原来的检测webdriver 注入js 的防护方式。随机切换到第二阶段即放过webdriver 的js 检测,动态混淆关键标签的ID ,NAME ,class name的值

什么时候动态变化?我们可以在upstream 第一次返回内容就开始,往后我们hook 一些关键事件,当页面触发这类事件时主动变化。

新的思路2:随机插入不可见相同标签

在分析Selenium中发现它在查找元素定位时不可见标签的会对其有干扰,在Selenium的git hub有提到过,高版本的Selenium 支持查找disabled 的标签。哪么我们就可以构造ID ,name 等属性一样的但是hidden隐藏的,disabled 不可操作的标签,可以成功干扰它的定位元素过程,并且界面UI不会察觉。这种思路作用于,自动化脚本不是通过ID,NAME来做元素定位,而是通过xpath使用 css selector来做定位的情况。

举个栗子:

201812051544019146607737.png

自动化工具通过xpath语法可以定位上面的搜索位置,上面的ID, name动态方式就无法干扰了。

上面图搜索框上面的hidden的是插入的虚假标签,

未插入前:羊毛程序通过下面的结构避开id,name完成定位

#main > header > div.header-content.clearfix > div.header-search-module > div.header-search-block > input

插入隐藏标签后:羊毛程序需要修改xpath结构,才能找到正确的输入位置。

#main > header > div.header-content.clearfix > div.header-search-module > div.header-search-block > input:nth-child(2)

这里插入的标签是hidden,disabled的,他UI不可见,不会被form表单提交到Server端的。完美的干扰了羊毛程序的查找。

新的思路3:text插入不可见字符/其他字符

上面的思路几乎干扰了webdriver最常用的元素定位方式,剩下的是通过link文本的方式进行定位。Selenium支持根据标签的text值或部分值进行搜索定位元素。我们在原始的text里插入不影响页面UI明显变化的特殊字符,就可以干扰webdriver的这种定位元素的方式。

新的思路4:化被动为主动的对抗思路

这里的反攻思路主要是利用浏览器的缺陷,利用自动化工具的bug,甚至漏洞来反击。这个比较有意思,我们通过GitHub上查看Selenium,Chromedriver, geckodriver的issues,收集广大网友们在自动化测试(薅羊毛)中发现的bug,我们故意构造这样的环境,迫使羊毛程序自己崩溃。用这种方式来干扰阻挡羊毛行动。

0x05 demo一下

长篇大论说了那么多,你一定心里发毛了“show me the code...”。基于上面的多种思路现在开始demo一下,我用mitmproxy模拟反向代理工具,编写python脚本来对原站返回内容做修改,插入特定的JS代码。插入的JS代码实现上面的思路和hook一些Window的事件比如onkeypress, onclick, onchange事件,当触发这些事件时页面上发生一些动态变化,这些动态变化UI上没有明显变化,但是影响了Selenium注入的JS脚本的自动化行为过程。

Demo1­:动态变化id,name值

mitmproxy脚本编写如下,给特定的域名,登录页面注入我的们JS脚本,并放在了body最后,应该能尽量减少对原页面的影响。

201812051544017569103738.png

注入的JS脚本太长限于篇幅不贴出来了,主要实现:

通过document.getElementsByTagName遍历input标签,记录所有标签的id,name,class name值,定义change 函数,用于随机生成新的id,name等值,hook一些事件,当触发事件后调用change函数实现一次动态修改。

最后当然还需要hook 表单提交的onsubmit函数,先还原真实的id,name,class name值,然后走正常提交。

注入JS后的效果:

当页面每一次触发事件,关键标签input的id,name进行一次变化。

201812051544018018166191.png

正常人类在无感知下完成业务的正常工作。

201812051544018322300778.png

使用自动化工具进行测试如下:

首先是没有经过mitmproxy的修改注入JS的,自动化登录成功如下图。

201812051544018394910059.png

然后访问经过代理后的注入JS的地址,自动化登录失败。

201812051544018454832802.png

根据console的提示,可以看出自动化工具在工作中因触发了一些事件,id, name动态变化了,程序无法定位完成对应的数据输入。

Demo2:插入不可见的相同标签

注入JS后的效果,插入了hidden,disabled,id,name等属性相同的标签:

201812051544019562214217.png

当然不能影响正常业务啦,如下:

201812051544019625790680.png

webdriver测试如下:下面是成功干扰了Firefox的程序,它说找到的元素不可用的。。

201812051544019677488025.png

Demo3:text插入不可见字符/其他字符

Selenium 类工具支持一种叫link 定位的操作,有时候不是一个输入框也不是一个按钮,而是一个文字链接,可以通过 link进行定位。

这种情况下,自动化程序不用id, name和xpath结构,直接通过文本查找匹配进行定位。我们为了干扰,能做的就是动态修改这些关键位置的文本,让其程序无法用于定位。

比如下面的测试代码,程序自动打开网页,通过“Sign In”找到登录链接,然后打开,通过“Contact Us”找到联系商家,然后通过“online form” 找到订单页面等。。这一些列自动化过程仅通过页面的文本来查找定位。前面的思路都无法对其干扰,我们如果将页面的“Sign In” 修改为“Sign . In”这样,做一些UI上尽可能小的变化动作。同样可能成功干扰自动化程序。

201812051544020237881426.png

对Webdriver成功干扰效果如下:

201812051544020448896634.png

这里仅能做到一个思路demo的证明,真正要做好还是不容易,因为对我来说还不知道怎么找到,浏览器上UI不显示,但是text是不同的修改办法。

跪求前端大佬指教。。

Demo4:化被动为主动的对抗思路

这里思路还没有完全构思好,用JS反攻客户端的好点子还没有。这里抛2个bug,怎么很好的用bug和JS反攻,期待和大佬的交流。bug地址:https://github.com/SeleniumHQ/selenium/issues/5840https://github.com/mozilla/geckodriver/issues/1228

0x06 总结

本文的思路早也有人提出过,写完本文后才知道早在16年携程就有前辈写文《关于反爬虫,看这一篇就够了》地址:https://blog.csdn.net/u013886628/article/details/51820221,提到过了。提及的内容截图如下:

201812051544021106525820.png

可见携程的反爬虫历史很悠长哦。晚辈献丑了,路漫漫其修远兮,吾将上下而求索。

抵现券一券多用问题原理与总结

背景

支付H5预计2015-12-31上线,故在2015-12-21日提测了一个服务端的安全测试单(单号:1632363),由安全测试人员bit4帮忙跟进测试。bit4于2015-12-26日于redmine系统提交了一个安全bug(单号:266626 【安全】【支付】抵现券高并发处理不当,可一券多用)。

测试步骤

  1. 通过PaySdk客户端,生成业务订单,并点击支付生成支付订单,但不进行购买,如此反复生成多个初始化的购买订单
  2. 通过抵现券查询接口(有三个,都可以),直接进行支付流程就会有这个接口的的返回信息,抓包查看即可,选取一个抵现券记录下它的code值,比如:d2404ac1b2a54a1aadad752b536fxxxx
  3. 截获支付流程中的购买接口(https://pay.xxx.com/pay/oauth/voucher/deal/buy),真正进行支付订单支付的接口,替换其中的code值为刚记录的值,并替换订单值为第一步骤中所记录的初始化订单的值。
  4. 同时发起订单号不同而抵现券值相同的多个请求,构成并发请求。之后查看结果。发现有两个请求均获得支付成功。

注:以上环境中,设置的订单金额和使用的抵现券的金额都是一元,以保证请求是有效的,不受其他逻辑的干扰。而且测试的时候资金账号基本余额也做了检查,余额未变动。而抵现券也只是少了一个。

现象

同一个抵现券在高并发请求下出现被多次使用成功,多个订单可以使用同一个抵现券购买成功。

核实问题

根据安全测试人员提供的订单号及抵现券code, 分析日志及DB中的订单状态,抵现券状态,确实发现两个不同的购买订单使用同一个抵现券code购买成功,且可重现。

分析问题出现原因

经分析代码AccountServiceImpl.decrVoucherFirst()方法发现调用消费抵现券方法VoucherServiceImpl.consumeVoucherCode()时未对方法是否消费抵现券成功做处理,而该方法本身也没有对是否成功消费抵现券做处理。

故导致,同一个抵现券在高并发请求下,有多个请求同时满足消费抵现券的前置条件,到达如下图二所标识部分,则多个请求都可以执行完该方法,只是返回值不同, 上层调用者如图一所示, 并未对返回结果做判断。

1.png

2.png

验证问题

修改VoucherServiceImpl.consumeVoucherCode()方法,不更改程序原有逻辑,仅添加详细日志来验证分析出的原因是否正确, 修改如下。

3.png

重新打包,提交到测试环境,请安全测试人员重新测试, 日志如下:

4-1.png

日志与预期结果相符,验证了猜测。
 
于2015-12-27日,修复了该bug, 修改如下图所未, 调用到更新DB时,更改失败时,抛出消费抵现券失败异常, 阻止程序继续执行。

5.png

重新打包,部署到测试环境,测试结果如下:

测试时发现有三个请求在同时尝试修改券的状态,其中两个返回了“消费抵现券失败”,只有一个成功。确认修复有效。

总结

Mysql处理高并发,防止库存超卖的问题,大部分人一般想到的都是事务,但是事务是控制库存超卖的必要条件,但不是充分必要条件。

举例:

  • 商品总库存:4个商品
  • 并发请求人:a、1个商品 b、2个商品 c、3个商品

假如产品表名为:t_store, 商品id为:12345, 商品库存字符为: amount, 请求减掉的库存数量: quantity

程序如下:

BeginTransaction(开启事务)
try{
    $result = $dbca->query('select amount from t_store where product_id = 12345');
    if(result->amount > 0){
        $dbca->query('update t_store set amount = amount - quantity where product_id = 12345');
    }
}catch($e Exception){
    rollBack(回滚);
}
commit(提交事务);
EndTransaction(结束事务)

以上代码就是我们平时控制库存写的代码了,大多数人都会这么写,看似问题不大,其实隐藏着巨大的漏洞。

数据库的访问其实就是对磁盘文件的访问,数据库中的表其实就是保存在磁盘上的一个个文件,甚至一个文件包含了多张表。
例如由于高并发,当前有三个用户a、b、c三个用户进入到了这个事务中,这个时候会产生一个共享锁(读锁,允许多个事务读,但是不允许修改),

---me:为什么这里加的是共享锁而不是排他锁呢?因为是先query,也就是读取,所以加共享锁。而下边的update是修改,只能是排它锁。

所以在select的时候,这三个用户查到的库存数量都是4个,同时还要注意,mysql innodb查到的结果是有版本控制(MVCC,有兴趣的同学可以自己百度)的,在其他用户更新没有commit之前(也就是没有产生新版本之前),当前用户查到的结果依然是旧版本。

然后是update,假如这三个用户同时到达update这里,这个时候Mysql会把update更新语句并发串行化,也就是给同时到达这里的是三个用户排个序,一个一个执行,并生成排他锁(可读可写,独占资源),在当前这个update语句commit之前,其他用户等待执行,commit后,生成新的版本;

这样执行完后,库存肯定为负数了。但是根据以上描述,我们修改一下代码就不会出现超买现象了,代码如下:

BeginTransaction(开启事务)
try{
    $dbca->query('update t_store set amount = amount - quantity where amount>= quantity and product_id = 12345');
}catch($e Exception){
    rollBack(回滚);
}
commit(提交事务);
EndTransaction(结束事务)

这样就可以控制库存超卖的情况了, 但是还需要处理一点,就是执行这个update事务究竟是否更新成功(即更新成功的records是否大于0)呢 , 上面的update更新成功才能够接着处理用户扣钱,等一系列的操作。

再来看看此次我们所犯的错误:

很明显所犯的正是没有关心update执行是否成功亦即更新成功的records是否大于0,在没有做任何的判断的情况下让程序继续向下执行。故而导致此bug的出现。

本内容选自小密圈:

20180613075812.jpg

验证码安全那些事

前言

最近在研究验证码安全,本文主要分析四种流行的验证码(图形,短信,语音和滑动)进行分析,写这篇文章的出发点并非是绕过或破解验证码,而是根据自身业务情况来选择对应的验证码类型,在用户体验和安全性中找到属于自己的平衡点。

有问题可与我联系Wechat:atiger77

目录

0×01. 图形验证码  
0×02. 短信验证码
0×03. 语音验证码
0×04. 滑动验证码
0×05. 总结

备注:无论使用哪种验证码,只要开发不当都可能存在安全漏洞,为了减少文章重复内容,只在短信验证码中讲解漏洞以及对应加固方案,在语音验证码中讲解风控预防措施。

0×01 图形验证码

图形验证码是出现最早也是使用最为广泛的验证码没有之一,从最早的简单验证码到现在各种五花八门的验证码,为了防止被识别有加噪点的,有加干扰线的,有加各种背景的,有加逻辑题的…

之所以会有越来越恶心的验证码主要是防止验证码被机器识别,所设计的验证码难度也在不断上升,相比较来看下面两张图就知道为什么会这么设计了。

一般识别流程:

14917152134767.png

一般验证码加固方法:

14917152549751.png

随着验证码难度的提高识别的成本也随即提高,为了进行识别测试,我分别收集了四家不同类型互联网公司的验证码,情况如下:

某招聘网站验证码 – 字母周围有噪点,字体扭曲

20170424112448.png

某电商网站验证码 – 不同样式,字母阴影,字母粘连,背景色干扰

20170424112458.png

某社交网站验证码 – 主体干扰线,背景色干扰,背景字母干扰,字体扭曲,字母粘连

20170424112509.png

某生活类网站验证码 – 背景干扰线,背景色干扰,背景字母干扰,字体扭曲,字母粘连,字体镂空

20170424112522.png

首先针对某招聘网站的验证码进行识别,这里基于tesseract写的一个OCR程序,测试样本10张,最后准确率在50%,反溯以下发现对识别相近字时会比较吃力比如”z”和”2″,”0″和”O”等等,因为验证码做了反识别部分验证码人眼也比较难识别,如果继续优化识别率达到70%左右应该问题不大。

14917154289847.png

接着对某电商网站的验证码进行识别,最后准备率在40%,由于该网站验证码难度各不相同对于这种类型的验证码比较难做统一处理,即使使用机器学习的方法一个模型也很难去识别所有的验证码。

后面两个网站的验证码使用图像识别去做难度就很高了,为了不被识别分别做了大量的反识别工作,那么对于这类验证码一般使用的是人工打码平台,之前有文章已经介绍过了这里就不重复描述了。

这是某打码平台的价格,网上可以找到很多类似的网站。

14917154282029.png

那么对于企业来说,怎么防止验证码被人工打码呢,从安全角度来说是比较难做到防止的,为了不失去用户体验每张验证码的失效时间并不会很短,对于一个熟练的人工打码人员来说完全不在话下,那么我们从验证码本身出发去提高攻击的一个成本,举一个例子,可以使用DataURI scheme的方法把验证码图片直接写到HTML文件中去增加识别的时间和成本,当然还会有很多方法可以去增加攻击成本。

如果想从根本去除打码平台的困扰,这里可以参考支付宝和微信这两个产品的验证码机制,在某些地方所弹出的图形验证码是和使用者有关,比如选择联系人头像,选择购买过的物品,对于人工打码的人来说是完全不知道答案的,但是用了这种类型的验证码也会出现问题,比如“好友”足够了解你的情况下(个人信息泄露太多)可能会成功的进入。

现在有很多文章会用到神经网络CNN的方法用来识别验证码,相比传统的识别用机器学习有了样本的学习,识别率会更加的准确,对一些人眼可能判断错误的字母机器或许能判断准确,不过学习成本也是很高的。

0×02 短信验证码

短信验证码常见于注册,忘记密码,确认下单等阶段,特别是一些涉及到用户个人敏感行为时候,为了确认操作是用户本人执行的通常会使用短信验证码进行二次认证。同时,短信验证码也是最为常见的验证码类型之一,这里总结一些验证码逻辑漏洞。

a. 接口没设频次上限导致短信轰炸

起因:短信轰炸问题往往出现在一些小站或者子站,这几年很少看到通过GET请求发送短信验证码了,基本都是使用POST请求,使用抓包软件可以重放请求对于后端没有做限制的网站就可以达到短信轰炸的效果。

危害:对用户来说个人生活受到骚扰,对企业来说造成一定的负面影响,很多小公司因为短信接口被大量调用出现运营问题,对于公司没有安全工程师的情况下,一般都是业务方发现了数据异常向上汇报,最后和开发一起反溯才会找到这个问题,那么在这段时间中对企业所损失的钱也是无法挽回的。

预防:这里主要是针对两种攻击场景来进行防御,第一种是对单用户的短信轰炸,即重放发送请求且phonenum为一个值。第二种是对多用户发送短信骚扰的场景,即将phonenum参数设置为字典,重放短信接口。

设置发送间隔,即单一用户发送请求后,与下次发送请求时间需要间隔60秒。

设置单用户发送上限,即设置每个用户单位时间内发送短信数的上限,如果超过阈值就不允许今天再次调用短信接口(阈值根据业务情况设置)。

设置单IP发送上限,这种情况是预防第二种攻击场景的,由于IP的特殊性可能存在所处IP是大出口,一旦误杀后果会很验证,所以这个限制根据自身情况酌情考虑,对于有风控的团队来说,当发现发送IP存在异常可以对该IP增加二次认证来防止机器操作,也可以降低误杀情况。

备注:b和c两个漏洞场景一般截图说明下开发就知道问题所在了,这里修复建议的内容写的就比较少。

b. 验证码内容包含在返回包中

起因:因为开发不严谨导致通过抓包可以看到验证码在回显中显示。

14917155261248.png

危害:由于验证码直接返回,通过该漏洞可以注册任意用户,重置已注册用户密码,修改绑定信息等高危操作,对用户造成一定影响。

修复建议:不要将短信验证码在回显中显示,验证码只存于服务端中并不能通过任何api直接获取。

c. 修改返回包内容绕过验证

起因:前端校验导致下列问题。

14917155974535.png

14917155573802.png

危害:可跳过正常校验逻辑。

修复建议:服务端严格做检验,和开发谈谈心。

假设网站验证码接口没有以上说的任何漏洞,那么短信认证是否安全呢?

在用户手机没有被种木马,用户手机没被强制降频到GSM等情况下,当攻击者无法获取短信验证码则某些关键操作是无法继续的,在一定程度上起到了拦截的作用。特别针对盗号的场景,由于设备或常用地发生了变化,在用户登录时候弹出验证码进行校验防止被盗号的情况。短信验证码只对部分场景可以起到防护作用,单一的认证对于企业来说无法阻止以下的场景。

14917155587746.png

这张图在我上一篇文章中出现过,这是在某打码平台注册了一个帐号的截图,从图上就可以看出虽然用了短信验证码,但是可以通过打码平台来自动获取并完成注册流程。

针对打码平台的垃圾注册场景也可以通过一些手段去增加批量自动化的注册成本,防御手法有相同之处,在语音验证码中会简单讲解。

0×03 语音验证码

相比短信验证码,语音验证码存在以下的优势:

a.语音验证对防刷单效果更显著;
b.语音验证码到达率更高;
c.用户体验更加友好;

为什么说语音验证码对防刷单更有效,这里贴一张某打码平台的收费情况,可以看到语音验证的价格是短信的十倍。很多公司是不会对17开头的虚拟号段进行发送的,为此卡商提供的卡号基本是实名认证的且没有停机的正常号段,所以收语音验证的成本就会比短信高很多。

14917156829962.png

这家打码平台对语音识别有两种方式,一个是系统识别,另一个是自己听取,对于系统识别平台解释是由听码团队负责听取验证码后传输文字回来,自己听取则是自己下载语音验证码录音自己听候识别。

跟公司研究移动端安全的小伙伴讨论了下,根据目前的技术要做到全自动也并非不可能,这里简单画了一个流程,从触发语音认证开始到最后的填入完成验证一条龙。

14917156821814.png

首先要获取语音认证,等语音验证码说完以后自动保存录音,进行语音分析,国内有几家厂商有语音识别的服务并都提供SDK,所涉及的服务可以用来进行识别,将语音转换成文字并提取验证码部分,完成最后验证。

对于企业来说我们如何防止这种垃圾注册的场景呢,说几种通用的思路供参考,这里主要可以分事前和事后基于规则和行为做判断。

对于事前,可以借助反欺诈接口检测请求的号码是否存在风险,WEB端判断用户UserAgent是否存在异常,APP端判断用户DeviceID是否存在异常,由于事前可以拿到的信息并不多,可以围绕可以拿到的参数做规则,对触发规则的用户进行阻断操作,可以在发送短信或者语音前让其先填写一次图形验证码,多一步校验。当然,也有公司会将部分手机号进行拉黑操作,在源头上禁止某些手机号进行注册。

对于事后,服务端校验以后就可以拿到状态等信息,围绕这些信息又可以制定一些规则,这里可以举一个通用的规则,比如单位时间内单IP登录失败超过阈值的对这个IP进行某某处理,这也是最基本的频次规则。对于不同的业务有着自己不同的策略,比如金融公司的APP会索取用户通讯录的权限,根据用户的好友做用户画像,某些公司也会做二度关系画像来判断用户是否存在风险。

这里不会说太多的规则和策略,由于公司的业务都不尽相同,当日志接入以后针对目前场景存在的问题去设计不同的规则即可。

0×04 滑动验证码

现在有不少的互联网公司开始使用滑动验证码进行校验,看似一个简单的滑动操作,背后都有风控引擎和相应的规则作为保障,首先讲一下滑动验证码的整个流程。

1491715758468.png

我们说一下正常的逻辑,首先用户滑动验证码到指定位置,完成后会给服务端回传各种加密信息,为了做风控规则来判断是否异常,个人猜测其规则会包含用户IP,操作行为路径,UA,COOKIE,设备指纹等等,如果没有命中规则,就会放行校验通过,如果命中规则就会弹出二次校验,只有通过校验后才可以放行。

那么如何破解滑动验证码呢,第一种是模拟正常用户操作,并让风控不要命中风控规则。第二种是尝试破解第二阶段的验证,对于图片可以使用OCR技术进行识别(针对类ReCAPTCHA验证码)。第二种的难度比较高,网上的文章也是针对第一种来进行破解,这里简单讲一下破解方法。

我对某商业滑动验证码进行了测试,整体流程梳理如下:

14917157589764.png

全部过程可以用自动化来实现,整体成功率要分为两个部分来看,第一是验证码自身风控能发现多少问题,我所测试的接口破解成功率在76%左右。第二是企业自身的风控规则能发现和阻断多少异常情况。两者结合最后的成功率就会有所下降,虽然有接口破解但是价格昂贵,已经提高了攻击成本,如果账户不涉及到金钱也不会考虑这么高的攻击成本,其实说到底还是成本和收益的一个衡量。

这里聊一下商业化滑动验证码厂商的优势和弊端。首先说优势,滑动验证码肯定会成为一种流行趋势,对于企业来说自研的成本比较大,无非也就是几个工程师参考已有的进行模仿,先不说效果开发流程上就会耽误人力,这点钱宁愿使用商业化的,现在的产品都已经相对成熟也有专门的团队进行升级维护,安全和体验性上来说都还不错。弊端到也谈不上,这里主要说几点使用前要考虑的地方,一个是响应延迟,当大量并发过来的时候是不是撑得住,最高响应时间是否有上限,如果超过是否会降级,如何降级等等,万一碰到不可预计的问题,比如碰到光缆被挖断,服务是否能即使切换,灾备是否完善等等,在购买前这些问题最好都要进行测试并有应急方案。

0×05 总结

前面也总结了很多验证码,我认为一个好的验证码首先不能有逻辑上的漏洞能绕过,否则再复杂的验证码都是形同虚设。其次,不能为了增加破解难度而抛弃用户体验,不要把验证码做的极其复杂人眼都识别不了,这种也会失去验证码的意义(用户都没了还给谁验证…)

目前来说大部分的企业还是偏向使用图形验证码,在测试过程中只有极少数的公司会动态升级自己的图片验证码,随着输错次数的上升验证码难度也随机上升。统一验证码的设计初衷是好的,即使攻击者使用了OCR技术进行破解,一旦失败数触发到阈值即自动上升图形验证码难度,增加破解成本。

为了防止验证码被爬虫获取后专门进行分析,针对性的破解攻击,需要准备多套图形验证码定期进行替换。

统一验证码体系如果只用单纯的规则去匹配所弹出的验证码,那么这种情况会出现不少的误杀,会让某些正常用户操作频次超过设定阈值,用户在尝试输入密码的过程中验证码越来越难,最后导致用户投诉。所以我认为一个完善的统一验证码体系应该由风控来判断是否触发规则,对触发规则的请求进行相应的验证升级,而不是设定一个死的阈值,对关键操作可以用多种验证码组合的形式来进行校验,常见的组合有,滑动+图形,图形+语音等。

就我个人而言,比较喜欢点触式和滑动式的验证码,通过采集用户当前各种的参数行为(行为轨距,操作时间,当前环境等等)来判断是否为机器行为。在用户体验上,手机端不是很建议使用选字类型的验证码,对于非大屏的手机不是很友好。在安全性上,这类验证码要比其他验证码破解成本高。我相信验证码的趋势也会逐渐向这种类型的靠拢。

下面的图片引用了google的ReCAPTCHA,分别截取图片和语音验证,ReCAPTCHA在安全性和用户体验做的都很好,即使这样还是被安全研究者破解过,所以不要完全依赖一种验证码,对敏感操作可以使用多种验证码。下面是官网的DEMO:

图片类:

14917158615511.png

声音类:

14917158896397.png

无论哪种验证码都有自己的适用场景,也没有一种绝对安全的验证码,根据自身业务情况来选择对应的验证码类型,在用户体验和安全性中找到属于自己的平衡点。最后,有做业务安全的同学可以一起学习交流。

业务安全冷知识

前言

年前突然想写一篇简单易读的文章,讲一点可能会被忽略的业务安全问题,这些问题都算不上是漏洞,但可以对公司造成一定的影响,所以我这里称他们为冷知识,当中会涉及一些竞品分析的点,部分内容对中小型互联网企业都通用。有问题可与我联系wechat:atiger77

序号

  1. 前端提示内容过多;
  2. 用户ID自增隐患;
  3. 域名到期时间;
  4. 短信接口任意调用;

前端提示内容过多

所谓提示内容过多,一般常见于登录重置密码等页面。以登录页面举例,当输入的用户名和密码不正确时,返回的内容过于详细,常见的有: a.你输入的用户名不存在 b.密码必须大于等于6位 c.你输入的密码不正确 等等内容。

排除逻辑漏洞的问题,当前端返回给攻击者越多的信息,对于攻击者而言也缩短了攻击者的成本,还是拿上面的情况来说,第一种,攻击者根据提示可以测试出用户覆盖率,第二种当登录处没有验证码机制的时候,对攻击者字段选择上能节省大量时间。

  • 造成原因:开发安全意识薄弱
  • 解决方法:去除不需要的回显内容

用户ID自增隐患

国内互联网情况就是看到别人的产品不错马上就拿过来抄,除了少数一些以技术为核心驱动的公司,其他的都可以在短期内搞一个看着一样的产品出来,那么这个安全冷知识在竞品分析中可以获取一定成果。

当公司业务线多起来后,必然要上用户中心集中管理用户,一般的设计逻辑都是单表存一千万的用户并且UID自增,那么这个风险点也会随之而来。

设想一下这个情况,你的公司有一家“孪生兄弟”,你又想知道他们的运营情况,那么可以尝试这样做,今天的8点注册一个号,抓包记录UID,在明天的8点再注册一个号也记录下UID,一直重复在同一时间点注册用户并记录UID,将后一天的UID减去前一天的UID即可得出一天的注册情况,当你产品运营数据被竞品所得到的时候,是不是很可怕呢。

  • 造成原因:UID顺序自增,规律性太容易被发现
  • 解决方法:混淆UID生成规则

域名到期时间

公司域名管理其实是算在运维部下的,为什么这里要说因为一直会看到新闻说某某公司因为域名到期忘记续费,导致域名被抢注的情况。

简单一提,需要定期的查看域名到期情况,对快到期的域名做续费处理,对于公司而言,买域名的钱和买白菜一样。所以,在域名快到期的时候尽快解决把。

  • 造成原因:到期域名未即时续费
  • 解决方法:记录域名到期时间

短信接口任意调用

这个问题也很普遍,短信接口的应用场景很多,用户注册、忘记密码、重置密码等等。之前也看到新闻说一家创业公司短信接口被恶意调用,无缘无故损失了一大笔钱。

往往公司发现这个异常都是通过周的数据统计或者是月的数据统计,那么这个时候已经无法挽回之前的损失了。

那么防范的措施也不复杂,增加验证码机制,设置短信发送上限,接口请求参数GET->POST等等。

  • 造成原因:没有校验过程
  • 解决方法:风控措施

总结

暂时就想到这么多,以后想到我还要继续添加进去,有问题可以和我联系。