Java SQL Inject
JDBC SQL Inject
JDBC 原生查询如果没有经过预编译,而是直接进行 SQL 语句的拼接,这时过滤不严格则会产生 SQL 注入问题,比如下面代码就是一个带有 SQL 注入漏洞的 Demo
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/security?characterEncoding=utf8&useSSL=false&serverTimezone=UTC", "root", "123456");
String sql = "select * from users where id = '"+id+"'";
resp.getWriter().write("The SQL statement:" + sql + "\n");
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
预编译修复
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/security?characterEncoding=utf8&useSSL=false&serverTimezone=UTC", "root", "123456");
String sql = "select * from users where id = ?";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
Mybatis SQL Inject
Mybatis 执行SQL语句也有预编译和直接拼接两种方法,如果直接拼接SQL语句且过滤不严格则会产生SQL注入问题,比如下面一个查询语句,使用了 ${}
直接拼接值
<select id="selectUserByName" resultType="com.mybatis.pojo.User">
SELECT * FROM Users WHERE username = '${username}'
</select>
使用 #{}
进行预编译则可以有效防御 SQL 注入问题
<select id="selectUserByName" resultType="com.mybatis.pojo.User">
SELECT * FROM Users WHERE username = '#{username}'
</select>
LIKE Query
对于 Like 查询,直接预编译则程序会抛出异常
<select id="selectUserByLikeName" resultType="com.mybatis.pojo.User">
SELECT * FROM Users WHERE username like '%#{username}#'
</select>
如果开发人员为了防止报错而改为直接取值且没有足够过滤则产生SQL注入问题
<select id="selectUserByLikeName" resultType="com.mybatis.pojo.User">
SELECT * FROM Users WHERE username like '%${username}%'
</select>
直接预编译会报错,那么就使用 concat 连接预编译,既不会抛出异常也防御了SQL注入
<select id="selectUserByLikeNameRepair" resultType="com.mybatis.pojo.User">
SELECT * FROM Users WHERE username like concat('%',#{username},'%')
</select>
IN/ORDER BY Query
同理,IN/ORDER BY
查询直接预编译也会使得程序抛出异常,如果开发人员改为直接拼接的方式构造SQL语句且没有足够过滤则会产生SQL注入
JavaWeb Inject Demo :https://github.com/ky0103/JavaSecurityDemo/tree/main/SQLInject
需要把 Myabtis/JDBC
依赖加入到 WEB-INF/lib
目录下
Java XSS
在 Java 中 XSS 的产生一般是在 Servlet 中获取到参数值,然后设置到 request
属性中,在 jsp 文件中就可以直接引用 request 中的值了,而如果没有对参数值进行过滤,那么就可能产生 XXS 问题。如下代码就没有进行过滤就设置属性值了
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
req.setAttribute("username",username);
req.getRequestDispatcher("/index.jsp").forward(req,resp);
}
XSS 防御可以利用 ESAPI
ESAPI(Enterprise Security API)是一个免费开源的Web应用程序API,目的帮助开发者开发出更加安全的代码,并且它本身就很方便调用。
依赖
<dependency>
<groupId>org.owasp.esapi</groupId>
<artifactId>esapi</artifactId>
<version>2.2.1.1</version>
</dependency>
在存储值到 request 中前用 ESAPI 进行过滤操作
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = req.getParameter("username");
ESAPI.encoder().encodeForJavaScript(username);
req.setAttribute("username",username);
req.getRequestDispatcher("/index.jsp").forward(req,resp);
}
JavaWeb Inject Demo :https://github.com/ky0103/JavaSecurityDemo/tree/main/XSS
需要把 ESAPI 依赖加入到 WEB-INF/lib
目录下
Java SSRF
HttpURLConnection 类的 SSRF 只支持 HTTP/HTTPS 协议,不支持 file 等协议
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String url = req.getParameter("url");
if (url != null){
URL url1 = new URL(url);
String htmlContent;
URLConnection urlConnection = url1.openConnection();
HttpURLConnection httpURLConnection = (HttpURLConnection)urlConnection;
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
StringBuffer html = new StringBuffer();
while ((htmlContent = bufferedReader.readLine()) != null){
html.append(htmlContent);
}
bufferedReader.close();
resp.getWriter().println(html);
}else {
resp.getWriter().write("?url");
}
}
URLConnection 支持 HTTP/HTTPS/file 等协议
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String url = req.getParameter("url");
if (url != null){
URL url1 = new URL(url);
String htmlContent;
URLConnection urlConnection = url1.openConnection();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
StringBuffer html = new StringBuffer();
while ((htmlContent = bufferedReader.readLine()) != null){
html.append(htmlContent);
}
bufferedReader.close();
resp.getWriter().println(html);
}else {
resp.getWriter().write("?url");
}
}
SSRF 下的文件下载,其实就是把响应头修改一下而已
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String fileName = "secret.txt";
resp.setHeader("content-disposition","attachment;fileName="+fileName);
String url = req.getParameter("url");
int length;
URL u = new URL(url);
InputStream inputStream = u.openStream();
byte[] bytes = new byte[1024];
while ((length = (inputStream.read(bytes))) > 0){
resp.getOutputStream().write(bytes,0,length);
}
}
SSRF 下对文件的读取,无法读取非图片类文件,但是可以探测文件是否存在
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String url = req.getParameter("url");
URL u = new URL(url);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
InputStream inputStream1 = u.openStream();
ImageInputStream imageInputStream = ImageIO.createImageInputStream(inputStream1);
BufferedImage read = ImageIO.read(imageInputStream);
ImageIO.write(read,"png",byteArrayOutputStream);
InputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
int length;
byte[] bytes = new byte[1024];
while ((length = (inputStream.read(bytes))) > 0){
resp.getOutputStream().write(bytes,0,length);
}
}
JavaWeb SSRF Demo :https://github.com/ky0103/JavaSecurityDemo/tree/main/XSS