写点什么

挖到项目中的 2 高危和中危漏洞

作者:悟空聊架构
  • 2025-05-14
    湖北
  • 本文字数:4097 字

    阅读完需:约 13 分钟

我正在参加 CodeBuddy「首席试玩官」内容创作大赛,本文所使用的 CodeBuddy 免费下载链接:腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴


你好,我是悟空。

一、背景

公司对系统安全非常中试,每个项目上线前都会做渗透测试和众测,如果在众测期间发现一个高危漏洞,就会奖励 5000 块奖金给发现该漏洞的同学。真的非常诱人啊!以至于有很多人晚上熬夜挖洞,有的一次就能挖几万块。


所以在做众测前, 我们都会对代码进行走查,看看有没有安全漏洞。


如果系统中的接口代码写得不严谨,就会造成很多安全漏洞,比如 SQL 注入漏洞、XSS 存储型漏洞。


本篇是工作中遇到的两个真实漏洞案例:


  • 乙方的系统中存在 SQL 注入漏洞,高危漏洞,我提前发现了。如果是我们自己的项目在众测阶段发现 SQL 注入漏洞,那就是 5000 块奖金了。

  • 我们的某个产品在众测阶段被报出 XSS 存储型漏洞,中危漏洞,奖金 2000 块,实打实发放提交人手中。


因为项目中有很多接口需要进行走查,排查起来是非常耗时的,这次我们来通过两个案例来演示如何借助 CodeBuddy 代码助手(一款 AI 工具)快速发现漏洞以及如何解决漏洞的。

二、IDEA 安装 CodeBuddy 插件。

还是熟悉的配方,熟悉的味道,在 IDEA 中搜索 CodeBuddy 插件安装。安装完成后重启 IDEA,然后登录 CodeBuddy 插件就可以开始使用了。


三、真实案例一:SQL 注入漏洞

3.1 全面分析代码中可能存在的漏洞

首先把乙方系统的后端代码文件喂给 CodeBuddy,让它帮我们分析该文件是否有安全漏洞,结果真的有安全漏洞。



CodeBuddy 会从以下几个方面开始分析:


  • SQL 注入漏洞

  • 权限控制问题

  • 输入验证

  • 敏感数据处理

  • 资源管理

  • 异常处理


经过代码审查,CodeBuddy 发现了几个严重的安全漏洞:


  1. SQL 注入漏洞(严重):

  2. 输入验证不足:

  3. 异常处理问题:

  4. 其他问题:

  5. SQL 语句中的表名"order"是 SQL 关键字,可能导致语法错误

  6. 没有进行适当的权限验证

  7. 没有使用预编译语句


3.2 分析 SQL 注入漏洞

这次我们重点来分析 SQL 注入漏洞。CodeBuddy 找到这一段代码是有问题的。



CodeBuddy 还给出了 SQL 注入的原因:直接将用户输入拼接到 SQL 语句中,容易受到 SQL 注入攻击。


漏洞详情:代码直接将用户输入的updateTime参数拼接到 SQL 查询语句中,没有进行任何参数验证或使用预编译语句。


攻击示例


' OR '1'='1 - 可能返回所有记录


'; DROP TABLE bd_material; -- - 可能删除整个表


'; UPDATE bd_material SET dr=1; -- - 可能篡改数据



最后 CodeBuddy 给出了修复建议:


使用预编译语句(Prepared Statement)防止 SQL 注入。修复后的代码如下:


// 使用预编译语句String sql = "select * from order where status = 0 and updateTime > ?";List<Map<String,String>> list = (List<Map<String,String>>) new BaseDAO()    .executeQuery(sql, new MapListProcessor(), new Object[]{updateTime});
复制代码


那为什么这段代码就能解决 SQL 注入的问题呢?

3.3 预编译语句如何防止 SQL 注入

预编译语句(Prepared Statement)如何防止 SQL 注入,以及为什么这种方式与直接字符串拼接有本质的区别?


带着这个问题,我们继续往下看:


  • 预编译语句的工作流程

  • 字符串拼接 vs 预编译语句

  • 预编译实际例子说明

  • 安全性保证

  • 性能优势

3.3.1 预编译语句的工作流程

3.3.1.1 预编译阶段

数据库先收到 SQL 语句模板,其中包含占位符?


数据库会预先编译这个 SQL 语句,生成执行计划;


此时 SQL 的结构已经确定,不会再改变;

3.3.1.2 参数绑定阶段

参数值作为数据传递给数据库


数据库将这些值严格作为数据处理,而不是 SQL 语句的一部分


即使参数中包含 SQL 关键字或引号,也只会被当作普通字符串处理

3.3.2 字符串拼接 vs 预编译语句

3.3.2.1 接字符串拼接(不安全)
// 不安全的方式String sql = "select * from order where status = 0 and updateTime > '" + updateTime + "'";
复制代码


这种方式中,SQL 语句和参数值在发送到数据库之前就完成了拼接。如果用户输入是:


2024-01-01' OR '1'='1
复制代码


最终的 SQL 语句会变成:


select * from order where status = 0 and updateTime > '2024-01-01' OR '1'='1'
复制代码


这样就改变了原始 SQL 的逻辑,造成了注入。

3.3.2.2 预编译语句(安全)
String sql = "select * from order where status = 0 and updateTime > ?";// ... executeQuery(sql, new MapListProcessor(), new Object[]{updateTime});
复制代码

3.3.3 预编译实际例子说明

假设用户输入恶意数据:2023-01-01' OR '1'='1

3.3.3.1 使用预编译语句时
String sql = "select * from order where status = 0 and updateTime > ?";// 实际执行时,数据库会将参数严格作为一个值处理// 相当于:select * from order where status = 0 and updateTime > '2023-01-01\' OR \'1\'=\'1'
复制代码


解释如下:


  • 整个输入被当作一个字符串值处理

  • 特殊字符会被自动转义

  • SQL 注入攻击失败

3.3.4 安全性保证

预编译语句提供三重保护:


  • 语句结构固定:SQL 语句结构在参数绑定前就已确定

  • 参数隔离:参数值与 SQL 语句完全分离处理

  • 自动转义:特殊字符自动进行安全处理

3.3.5 性能优势

除了安全性,预编译语句还有性能优势:


  • SQL 语句只需编译一次

  • 可重复使用执行计划

  • 减少数据库解析负担


因此,使用预编译语句不仅是安全性的需求,也是性能优化的最佳实践。

四、真实案例二:XSS 存储型漏洞

4.1 一个陌生的名字

XSS 存储型漏洞,估摸着很多同学都没有听过,这里简单给大家解释下。


XSS 攻击:跨站脚本攻击(Cross Site Scripting),为不和前端层叠样式表(Cascading Style Sheets)CSS 混淆,故将跨站脚本攻击缩写为 XSS。


**XSS(跨站脚本攻击)**是指恶意攻击者往 Web 页面里插入恶意 Script 代码,当用户浏览该页时,嵌入其中 Web 里面的 Script 代码会被执行,从而达到恶意攻击用户的目的。类似于 sql 注入。是目前最普遍的 Web 应用安全漏洞,也是 Web 攻击中最常见的攻击方式之一。

4.2 XSS 分类

4.2.1 存储型 XSS

存储型跨站脚本攻击也称为持久型跨站脚本攻击,是一种最具破坏性的跨站脚本攻击。注入的脚本永久存储在 Web 服务器上,如数据库、内存或文件系统中。只要注入脚本代码没有被清理,每次用户访问网页时都将加载恶意脚本。


原理如下图所示:



存储型跨站脚本攻击一般发生在论坛、博客、留言板等允许用户共享内容的网站,如果网站存在漏洞,未严格校验用户输入内容就可能被攻击者利用。攻击者利用发帖、评论、修改个人信息等功能提交恶意脚本代码到 Web 服务器进行保存,那么只要访问对应网页的用户都会在不知情的情况下读取恶意脚本。


相对于反射型跨站脚本攻击,存储型跨站脚本攻击影响范围更大,危及所有访问者。当然这种攻击方式也更难执行,攻击者需要找到可以利用的漏洞。

4.2.2 反射型 XSS

反射型跨站脚本攻击是最常见和最简单的攻击形式。所谓反射,是指此种类型的注入脚本必须被包含在发往 Web 服务器的请求中,然后 Web 服务器以某种方式反射到用户浏览器执行。也就是注入脚本作为客户端的请求提交给 Web 服务器,服务器解析后在响应消息中返回脚本由浏览器执行。因此攻击者需要使用钓鱼邮件、弹窗链接以及其他社会工程学方法引诱用户点击链接向 Web 服务器发出请求。


原理如下图所示:



攻击者往往在合法的 URL 末尾添加恶意代码构造链接,例如:


https://example.com/index.php?user=<script>恶意代码</script>
复制代码


用户点击此链接后,因为浏览器信任该网站,将执行恶意脚本代码。


从以上介绍可以看出,这种攻击类型 Web 服务器端无需存储注入脚本,直接通过 HTTP GET 或 POST 请求就可以完成攻击,实施比较简单。但是这种攻击方式需要由每个受害者触发才能发生攻击,往往也称为非持久型跨站脚本攻击。

4.2.3 DOM 型 XSS

DOM 型 XSS,是指攻击者在客户端对 DOM 中文档节点的属性、触发事件等进行修改以添加恶意 JavaScript 脚本,并使脚本在本地浏览器中执行。整个过程中没有服务器的参与,因此,DOM 型 XSS 漏洞是一个纯客户端的安全漏洞。


原理如下图所示:



基于 DOM 的跨站脚本攻击与反射型、存储型跨站脚本攻击有很大不同,整个攻击过程均在用户侧浏览器执行,无需 Web 服务器端进行解析和响应访问请求。这种类型的攻击比较难排查,WAF 设备无法获取通信流量,可能难以发现攻击。

4.2.4 总结

反射型 XSS 和 DOM 型 XSS 都属于“非持久型 XSS”,恶意脚本只执行一次;而存储型 XSS 则会将恶意脚本存储到后端服务器的数据库或文件中,每次访问页面时,脚本都会执行一次。

4.3 模拟 XSS 存储型漏洞攻击

正常页面

用户打开系统主页时,会看到如下页面,该页面显示正常。logo 图片的 base64 编码来源与后端返回的数据。



页面的前端代码简化后如下:


<img src="从后端获取的 logo base64 编码">test</img>
复制代码


前端页面将获取到的 logo base64 编码数据组装成的 HTML 如下图所示:


4.3.2 模拟黑客攻击

在众测阶段,产线会提前分配账号给大家登录测试,有测试同学登录后,用 postman 工具调用了一个保存请求,将 logo 存储为非正常图片的 base64 编码(注入了恶意的代码)。如下图所示:



这段 base64 编码对应的 JavaScript 代码如下:


<body onload=alert(1)>
复制代码


将这段 JavaScript 代码通过 base64 编码工具转换成 base64 编码,如下图所示:



用户访问主页时,浏览器会从 Web 服务器获取 logo 的图片数据,然后组装到 HTML 中,如下图所示:



最后用户看到的将是这个页面。



鼠标右键点击 test 左侧的图标,选择“在新标签页中打开图片”。



接着就会出现一个弹框,弹出了一个数字 1,表示 XSS 攻击成功了。


4.4 CodeBuddy 协助找安全漏洞

提示语:/Users/wukong/Documents/test.html 可能存在的安全漏洞问题


CodeBuddy 回答如下,第一句就是 XSS 漏洞风险,还是挺智能的。



修复方案如下:



  1. 结构改进:


  • 添加了完整的 HTML5 文档结构

  • 规范了页面编码声明


  1. 安全增强:


<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src 'self' data:;">
复制代码


  • 实施了严格的内容安全策略

  • 为图片添加了跨域安全属性

  • 添加了 referrer 控制策略


然后我直接让 CodeBuddy 帮我把文件改好。



这样就把这个 XSS 漏洞修复了,是不是很丝滑!

五、总结

通过第一个真实案例,我们利用 CodeBuddy 分析了代码中存在的 SQL 注入漏洞以及形成原因,然后通过预编译的方式来解决了 SQL 注入问题。


通过第二个真实案例,我们学习了 XSS 漏洞的三种类型,并利用 CodeBuddy 分析了代码中存在的 XSS 漏洞以及解决方案。

发布于: 刚刚阅读数: 4
用户头像

用故事、大白话讲解Java、分布式、架构设计 2018-05-06 加入

公众号:「悟空聊架构」 【个人博客】www.passjava.cn 【开源项目】基于 SpringCloud 的一套面试刷题系统 【Github】https://github.com/Jackson0714/PassJava-Platform

评论

发布
暂无评论
挖到项目中的2高危和中危漏洞_悟空聊架构_InfoQ写作社区