1 JavaWeb基础
1.1 JSP是什么
JSP全称Java Server Pages,是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。标签通常以<%开头以%>结束。
JSP是一种Java servlet,主要用于实现Java web应用程序的用户界面部分。网页开发者们通过结合HTML代码、XHTML代码、XML元素以及嵌入JSP操作和命令来编写JSP。
JSP会被编译成一个java类文件
,如index.jsp在Tomcat中Jasper编译后会生成index_jsp.java
和index_jsp.class
两个文件。
1.2 JSP生命周期
用户从点击URL发起request请求到response响应的JSP生命周期如下:
1.3 jar包与war包的区别
JAR(Java Archive,Java 归档文件)是与平台无关的文件格式,它允许将许多文件组合成一个压缩文件。JavaSE程序可以打包成Jar包。
war是一个可以直接运行的web模块,通常用于网站,打成包部署到容器中。以Tomcat来说,将war包放置在其\webapps\目录下,然后启动Tomcat,这个包就会自动解压。
war包内容
1.4 JSP内置对象
不需要用户提前定义,可以直接使用的。对内置对象的使用要考虑有没有对传来的数据进行过滤。
1.5 JavaWeb分层思想
视图层(View 视图)
控制层(Controller、Action 控制层)
服务层(Service)
业务逻辑层BO(business object)
实体层(entity 实体对象、VO(value object) 值对象 、模型层(bean)
持久层(dao- Data Access Object 数据访问层、PO(persistant object) 持久对象)
1.6 模块化开发
如今的较为大型的Java Web项目通常都采用了模块化方式开发,借助于Maven、Gradle依赖管理工具,Java可以非常轻松的完成模块化开发。
使用Maven开发的JavaWeb项目示例:
1.7 Servlet
Servlet来处理一些较为复杂的服务器端的业务逻辑。
值得注意的是在Servlet3.0之后(Tomcat7+)可以使用注解方式配置Servlet了。
基于注解的Servlet:
基于配置实现的Servlet:
2 JAVA代码审计工具
编写Java代码可以使用Myeclipse,Intellidea。
2.1 Intellidea
同样代码审计也可以用这些工具,这里介绍Intellidea,因为有免费版,下面是几个调试中会用到的几个快捷键:
- F7 ,进入下一步,如果当前断点是一个方法,进入方法体。
- F8 ,进入下一步,但不会进入方法体内。
- Alt+Shift+F7 , 进入下一步,如果当前断点是一个方法,方法还有方法则循环进入。
- Shift+F8 ,跳出到下一个断点,也可以按F9来实现。
- Drop Frame ,当进入一个方法体想回退到方法体外可以使用该键。
2.2 Fortify
在这里介绍一个比较不错的专用工具Fortify SCA。Fortify SCA是一款代码审计工具,也做白盒测试工具用,内置的分析引擎、安全编码规则包、审查工作台、规则自定义编辑器和向导、IDE 插件五部分组成专门,用来检测源代码中的漏洞!
工具主界面:
可以看一下fortify的rules库,这里是进行漏洞规则匹配的地方:
查看漏洞详情:
2.3 JAR
在某些特殊的场景下拿到的只是jar文件,我们任然可以反编译jar文件进行代码审计。常见的反编译工具:
- Fernflower可以很轻松的实现jar的完整反编译,执行如下命令即可: java -jar fernflower.jar
- JD-GUI是一个带GUI的反编译工具,在JD-GUI的菜单中点击File-->Save All Sources即可反编译jar。
- IDEA默认就支持jar包反编译。
- FernFlower提供了GUI版本Bytecode-Viewer,Bytecode-Viewer提供了直接反编译的class、jar、zip、apk、dex功能,直接拖拽jar就可以直接对整个jar进行反编译了。
3 代码审计
审计的漏洞类型分为业务安全
问题、代码实现
和服务框架
安全问题。
3.1 业务安全类
主要理解该系统的逻辑:
- 用户登陆、用户注册、找回密码等功能中密码信息未采用加密算法、未添加验证码、验证码未作安全刷新(未刷新Session中验证码的值)。
- 找回密码逻辑问题(如:可直接跳过验证逻辑)。
- 动态验证码
未限制验证码失败次数
、验证码有效期
、验证码长度过短
导致的验证码爆破问题。 - 充值、付款等功能调用了第三方支付系统未正确校验接口(与第三方的交互、与客户的交互,主要查看逻辑问题)。
- 用户中心转账、修改个人资料、密码、退出登陆等功能未采用验证码或
Token机制
导致存在CSRF漏洞
- 重要接口采用
ID自增、ID可预测并且云端未验证参数有效性
导致的越权访问、信息泄漏问题(如:任意用户订单越权访问)。 - 敏感信息未保护,如
Cookie中直接存储用户密码等重要信息
,跟踪cookie中的变量最终到了哪。 - 弱加密算法、弱密钥,如勿把Base64当成数据加密方式、重要算法密钥采用弱口令如
123456
。 - 后端无异常处理机制、未自定义50X错误页面,服务器异常导致敏感信息泄漏(如:数据库信息、网站绝对路径等)。
3.2 服务框架类
3.3 代码实现类
- 任意
文件读写
(文件上传、文件下载)、文件遍历
、文件删除
、文件重命名
等漏洞 - SQL注入漏洞
- XXE(XML实体注入攻击)
- 表达式执行(SpEL、OGNL、MVEL2、EL等)
- 系统命令执行漏洞(ProcessBuilder)
- 反序列化攻击(ObjectInputStream、JSON、XML等)
- Java反射攻击
- SSRF攻击
- XSS
1) 前端审计
前端经常出现漏洞的地方是JS和DOM事件,在这里会对前端输入的数据进行初步校验。
键盘事件
- onkeydown :某个键盘按键被按下
- onkeypress:某个键盘按键被按下并松开
- omkeyup:某个键盘按键被松开
鼠标事件
- onclick
- onmousemove
- ondblclick
- onmouseout
- onmousedown
其他事件
- 框架/对象事件
- 打印事件
- 表单事件
- 拖动事件
- 剪贴板事件
- 多媒体事件
2) Web.xml文件
审计时,得先看是否含有全局过滤器,经常是查看web.xml文件里的filter。过滤器Filter是实现了javax.servlet.Filter接口的服务器端程序,主要的用途是过滤字符编码、做一些业务逻辑判断等。
审计web.xml文件
首先我们可以看到使用了框架strust2和Hibernate,然后我们可以搜寻了这些框架存在的漏洞,在这里是否依旧存在。
其次可以查看filter,看看有没有对用户输入数据进行过滤的filter,发现没有关于xss、sql的过滤文件,那就说明所有提交到后台的数据都可能存在危险,从中找找纰漏。
3)XSS
XSS是跨站脚本攻击(Cross Site Script) 。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该网页时,嵌入其中Web里面的html代码会被执行,从而达到恶意用户的特殊目的。
XSS危害:
原理:
审计:
反射型xss,就是直接能从URL中获取数据,比如el表达式。如果参数是从后台获取的,并且在前端无法修改,那么就不存在反射性XSS。我们可以通过检查JAVA代码来验证此参数是不是从后台获取?
如下图,El表达式直接从前端获取数据:
看一个从后台获取数据的案例:
设置了value。
查看数据处理的servlet,发现他的数据是从后台获取的。
DOM型xss,直接使用DOM获取值,然后直接拼接到URL里,很容易出现漏洞。
4) SQL注入
- 直接拼接错误:
将request.getParameter("")
直接放在SQL语句。
- 预编译使用有误:
在使用占位符后未进行setObject或者setInt或者setString。
-
%和_
没有手动过滤%。预编译是不能处理这个符号的, 所以需要手动过滤,否则会造成慢查询,造成 dos
- Mybatis 框架
使用注解或者xml将java对象与数据库sql操作对应。在注解中或者 Mybatis 相关的配置文件中搜索 $ 。然后查看相关 sql 语句上下文环境。
与使用JDBC不同的是,MyBatis使用#{}
和${}
来进行参数值替换。
使用#{}
语法时,MyBatis会自动生成PreparedStatement
,使用参数绑定(?
)的方式来设置值,因此#{}
可以有效防止SQL注入。
XML例子
映射器界面
@Mapper
public interface UserMapper {
User getById(int id);
}
XML配置文件
<select id="getById" resultType="org.example.User">
SELECT * FROM user WHERE id = #{id}
</select>
使用${}
语法时,MyBatis会直接注入原始字符串,即相当于分段字符串,因此会导致SQL注入。
注释示例
@Mapper
public interface UserMapper {
@Select("SELECT * FROM user WHERE id= #{id}")
User getById(@Param("id") int id);
}
安全的写法是使用参数化查询,即SQL语句中使用参数绑定 ?(占位符)和PreparedStatement
,如
String sql = "SELECT * FROM users WHERE name= ? ";
PreparedStatement ps = connection.prepareStatement(sql);
// 参数 index 从 1 开始
ps.setString(1, name);
注意,?(占位符)不能用于order by,from
5)SSRF
代码中提供了从其他服务器应用获取数据的功能但没有对目标地址做过滤与限制。比如从指定URL链接获取图片、下载等。
常出问题的函数:
HttpClient.execute
HttpClient.executeMethod
HttpURLConnection.connect
HttpURLConnection.getInputStream
URL.openStream
常支持的协议:
6)CSRF
一些增删改查方法,是否进行Referer头检验
、token检验
无法构造的随机数参数
、验证码密码
。
7)序列化
Java 程序使用 ObjectInputStream 对象的readObject
方法将反序列化数据转换为 java 对象。但当输入的反序列化的数据可被用户控制
,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码
。
// 将序列化对象写入文件object.db中
FileOutputStream fos = new FileOutputStream("object.db");
ObjectOutputStream os = new ObjectOutputStream(fos);
os.writeObject(obj);
os.close();
// 从文件object.db中读取数据
FileInputStream fis = new FileInputStream("object.db");
ObjectInputStream ois = new ObjectInputStream(fis);
// 通过反序列化恢复对象obj
Object obj2 = ois.readObject();
System.out.println(obj2);
ois.close();
java 序列化的数据一般会以标记(ac ed 00 05
)开头,base64 编码后的特征为rO0AB
。
找出反序列化函数调用点:
- ObjectInputStream.readObject
- ObjectInputStream.readUnshared
- XMLDecoder.readObject
- Yaml.load
- XStream.fromXML
- ObjectMapper.readValue
- JSON.parseObject
8)命令执行
查找是否有使用如下方法,且其中的内容用户可控。
Runtime.exec
ProcessBuilder.start
GroovyShell.evaluate
9)id越权
在每个request.getParameter("userid");
之后查看是否有检验当前用户与要进行增删改查的用户。
安全学习交流群:687398569