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语句也有预编译和直接拼接两种方法,如果直接拼接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 查询,直接预编译则程序会抛出异常
<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
查询直接预编译也会使得程序抛出异常,如果开发人员改为直接拼接的方式构造SQL语句且没有足够过滤则会产生SQL注入
JavaWeb Inject Demo :https://github.com/ky0103/JavaSecurityDemo/tree/main/SQLInject
需要把 Myabtis/JDBC
依赖加入到 WEB-INF/lib
目录下
在 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
目录下
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