Java SQL Inject/XSS/SSRF

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 SQL Inject/XSS/SSRF

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 SQL Inject/XSS/SSRF

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

上一篇:urllib


下一篇:Axios前后端异步请求库 后端人员这一篇就够了 从0-0.5的知识掌握~