第33天:安全开发-JavaEE应用SQL预编译Filter过滤器Listener监听器访问控制
时间轴:
33天学习内容:
内存马介绍:(转存于浅析JavaWeb内存马基础原理与查杀思路-CSDN博客)
现在所流行的内存马也是有使用了filter和listener
SQL预编译:
危险写法:
sql预编译使用代码:(运行时使用第二个执行符号)
public class NewsServlet {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//接受用户输入的 变量字符串s接受
Scanner scanner=new Scanner(System.in);
System.out.print("please input id:");
String s=scanner.nextLine();
System.out.println(s);
Class.forName("com.mysql.jdbc.Driver");
String url ="jdbc:mysql://localhost:3306/demo1";
Connection connection = DriverManager.getConnection(url,"root","root");
System.out.println(connection);
//不安全的写法
String sql="select * from news where id="+s;
System.out.println(sql);
Statement statement= connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
造成结果SQL注入:
注入语句为:
1 union select 1,2,3,version(),user(),database()
安全写法:
安全写法代码如下(数据库内容遍历省略)
//预编译写法
String safesql="select * from news where id=?";
PreparedStatement preparedStatement=connection.prepareStatement(safesql);
preparedStatement.setString(1,s);
ResultSet resultSet=preparedStatement.executeQuery();
System.out.println(safesql);
代码解析如下:
// 定义了一个安全的SQL查询字符串,其中包含一个参数占位符(?),用于后续绑定具体的值
String safesql="select * from news where id=?";
// 使用connection对象(通常是一个已经建立好的数据库连接)来创建一个PreparedStatement对象
// PreparedStatement对象允许你设置SQL语句中的参数,并且这些参数在发送到数据库之前会被自动地转义,从而防止SQL注入攻击
PreparedStatement preparedStatement=connection.prepareStatement(safesql);
// 通过setString方法设置SQL语句中第一个参数(?)的值为变量s的值
// 这里假设s是一个已经定义好的String类型的变量,它包含了要查询的新闻ID
preparedStatement.setString(1,s);
// 执行PreparedStatement对象中的SQL查询,并返回一个ResultSet对象
// ResultSet对象包含了查询结果的所有行,你可以通过遍历ResultSet来访问每一行的数据
ResultSet resultSet=preparedStatement.executeQuery();
// 打印原始的SQL模板字符串,但请注意,这里打印的safesql并不包含已经绑定的参数值
// 它仅仅是定义时的字符串:"select * from news where id=?"
System.out.println(safesql);
// 如果需要处理查询结果,应该在这里添加遍历ResultSet的代码
// 例如:
// while(resultSet.next()) {
// // 处理每一行数据
// String title = resultSet.getString("title"); // 假设news表有一个名为title的列
// System.out.println(title);
// }
// 注意:在实际应用中,还需要添加异常处理代码来捕获和处理可能发生的SQLException
// 以及在代码的最后关闭ResultSet、PreparedStatement和Connection对象,以释放数据库资源
结果如下:
注入语句为: 1 union select 1,2,3,version(),user(),database()
1 a,1 and 11=11 都有数据正常回显。
22这种没有回显,sleep()是延时回应,输入后没有影响。
预编译绕过:
需要特定的条件,和warf一样
Fliter过滤器:
原理:
内存马就是写在代码中,没有写在文件里。内存马在listener和filter,而不是在后门Servlet里面。
测试步骤:
1.创建过滤器(可以删掉.mvn,.gitgnore,mvnw,mvnw.cmd使代码更简洁)
2.创建TestServlet,在其可以使用alt + insert进行测试重写(选择重写方法)等
参考文章:史上最全的IDEA快捷键总结_idea 变量全选-CSDN博客
3.路由使用时需要配置web.xml(配置方法上期说过)或在代码前加入@WebServlet("/test")
4.运行尝试:(使用/test?code=<script>alert(1)</script>)
//以下为TestServlet
//@WebServlet("/test")
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String code = req.getParameter("code");
PrintWriter out = resp.getWriter();
out.println(code);
out.flush();
out.close();
}
}
//以下为web.xml
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.example.filterdemo01.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
使用:
1.可以新建软件包进行分类(右键新建软件包)(很多东西会帮你重构,从而运行不会失败)。
2.extends和implements的区别(extends是继承类,implements是继承接口,也就是上下级的关系)
public class Xssfilter implements Filter {
}
以及
public class Xssfilter extends HttpServlet implements Filter {
}
3.创建Xssfilter,放在filter下。
代码步骤:
package com.example.filterdemo01.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter("/test")
public class Xssfilter implements Filter {
@Override
//中间件开启后就自动运行
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("xss正在过滤");
}
@Override
//doFilter 访问路由触发的方法
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("xss开启过滤");
}
@Override
//中间件关闭后就自动运行
public void destroy() {
System.out.println("xss销毁过滤");
}
}
当有servlet时web.xml就不需要配置了。
<filter>
<filter-name>Xssfilter</filter-name>
<filter-class>com.example.filterdemo01.filter.Xssfilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Xssfilter</filter-name>
<url-pattern>/test</url-pattern>
</filter-mapping>
运行结果:
没有访问/test就有了语句“xss开启过滤",原因是先通过filter,再到servlet。
访问/test,xss正在过滤。
XSS过滤:
代码:
package com.example.filterdemo01.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter("/test")
public class XssFilter implements Filter {
@Override
//中间件启动后就自动运行
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("xss开启过滤");
}
@Override
//中间件关闭后就自动运行
public void destroy() {
System.out.println("xss销毁过滤");
}
@Override
//doFilter 访问路由触发的方法
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("xss正在过滤");
//过滤代码就应该在放行前
//如果符合就放行,不符合就过滤(拦截)
//XSS过滤 接受参数值 如果有攻击payload 就进行拦截
// 接受参数值 如果没有攻击payload 就进行放行
HttpServletRequest request= (HttpServletRequest) servletRequest;
String code = request.getParameter("code");
if(!code.contains("<script>")){ //没有攻击payload
//放行
filterChain.doFilter(servletRequest,servletResponse);
}else{
System.out.println("存在XSS攻击");
//继续拦截
}
}
}
效果:
当运行不了的时候改一下tomcat的端口,改为8888:
AdminFIilter和AdminServlet:
代码:(在filter下创建AdminFIilter)
package com.example.filterdemo01.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter("/admin")
public class AdminFileter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("admin身份检测开启");
}
@Override
public void destroy() {
System.out.println("admin身份检测销毁");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("admin身份检测进行");
//检测Cookie过滤
HttpServletRequest request= (HttpServletRequest) servletRequest;
Cookie[] cookies=request.getCookies();
//对Cookie进行遍历获取
for(Cookie c:cookies){
String cName = c.getName();//获取cookie名
String cValue = c.getValue();//获取cookie值
System.out.println(cName);
System.out.println(cValue);
if(cName.contains("user") && cValue.contains("admin")){
filterChain.doFilter(servletRequest,servletResponse);
}else {
System.out.println("非管理员访问");
}
}
}
}
代码:(在servlet下创建Admin)
package com.example.filterdemo01.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/admin")
public class AdminServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("欢迎进入管理员页面");
}
}
运行效果:
当只访问http://localhost:8888/FilterDemo01/admin
当修改了cookie再次访问时:
后门内存码植入:
1.使用哥斯拉生成1.jsp:
2.注意:别将其放入WEB-INF中:
3.连接测试:
分析:
点击addFilterShell
点击getALLFilter:
总结:
内存马是写在Filter中:
后门没有写到Servlet中,常规方法扫描只会扫Servlet,而忘记扫描Filter。
Listener监听器:
参考: JavaWeb监听器_javaweb 监听器-CSDN博客
-监听 ServletContext、HttpSession、ServletRequest 等域对象创建和销毁事件
-监听域对象的属性发生修改的事件
-监听在事件发生前、发生后做一些必要的处理
1、创建监听器
2、监听器内置方法
3、监听器触发流程
@WebListener
<listener>
......
</listener>
4.监听器安全场景:
代码审计中分析执行逻辑触发操作,后门内存马植入等。
ListenerDemo01:


CSession:(生成)
package com.example.listendemo1.Servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
@WebServlet("/cs")
public class CSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet里面创建Session");
//创建Session
req.getSession();
}
}
DSession:(销毁)
package com.example.listendemo1.Servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/ds")
public class DSession extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet里面销毁Session");
//销毁Session
req.getSession().invalidate();
}
}
ListeneSession:
package com.example.listendemo1.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
@WebListener
public class ListenSession implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent se) {
//监听检测有Session创建就会执行这里
System.out.println("监听器监听到了session创建");
}
@Override
public void sessionDestroyed(HttpSessionEvent se) {
//监听检测有Session销毁就会执行这里
System.out.println("监听器监听到了session销毁");
}
}
效果图:/cs创建,/ds销毁
本文章由李豆豆喵和番薯小羊卷~共同完成。