接上文servlet:内存马--是一种无文件、存活于服务器内存中的恶意程序,其中Filter型内存马因其隐蔽性和拦截请求的能力,成为常见的攻击手段,与传统得一句话木马相比,内存马得恶意代码直接注入Web服务器进程内存(如Tomcat、Spring容器)中运行,仅通过内存驻留实现远程控制。
在Java Web中,Filter(过滤器)是Servlet规范中定义的一种组件,它可以对Web应用程序的请求和响应进行预处理和后处理。Filter在请求到达Servlet之前拦截请求,在响应返回给客户端之前拦截响应。Filter可以用于很多场景,比如日志记录、安全控制(比如常见的XSS防护等)、字符编码转换、数据压缩等。
认证和授权:例如,检查用户是否登录,是否有权限访问某个资源。
日志记录:记录请求的详细信息,如IP地址、请求的URL、处理时间等。
数据转换:如统一设置请求和响应的字符编码。
压缩响应:对响应内容进行压缩,减少网络传输量。
加密解密:对请求或响应进行加密解密处理。
触发事件:在请求处理前后触发一些事件。
1.启动Web应用时:容器(如Tomcat)会读取web.xml(或通过注解@WebFilter)中配置的Filter,并按照配置的顺序加载和初始化Filter。每个Filter都会调用其init方法,进行一些初始化操作。
2.请求到达时:当请求到达容器时,容器会根据web.xml中配置的URL映射,决定哪些Filter需要被执行。这些Filter会按照配置的顺序组成一个过滤器链(FilterChain)。
3.执行过滤器链:容器会依次调用链中每个Filter的doFilter方法,并将请求和响应对象作为参数传递,同时传递过滤器链对象。在Filter的doFilter方法中,开发者可以对请求和响应进行处理,然后决定是否继续执行过滤器链。通过调用FilterChain.doFilter(request, response)可以将请求传递给下一个Filter或最终的Servlet。如果在某个Filter中没有调用FilterChain.doFilter,那么请求就不会继续传递,即后续的Filter和Servlet都不会执行。
4.响应返回时:当过滤器链执行完毕,响应会以相反的顺序再次经过这些Filter(这里指的是在doFilter方法中,调用FilterChain.doFilter之后的代码,这些代码会在响应返回时执行)。因此,Filter可以对响应进行后处理。
既然知道了filter的作用,现在通过实例代码来看看具体的filter如何实现:
通过idea创建一个web项目:具体配置见上一篇servlet内存马内容,项目结构如下:
首先要写一个filter类必须继承HttpFilter,然后重写它的doFilter方法,在方法中实现我们自己的代码逻辑即可,新建一个filter类,firstFilter.java,代码如下:在下面的测试代码中,测试简单的防御xss攻击,当get请求中包含script,即返回一句话。
package com.ex; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpFilter; import java.io.IOException; public class firstFilter extends HttpFilter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String cmd = request.getParameter("cmd"); if (cmd.contains("script")){ response.getWriter().println("XSS--atack"); }else { super.doFilter(request, response, chain); } } @Override public void destroy() { } }
然后在web.xml中写入对应的filter配置信息,如下图:<url-pattern>/*</url-pattern>表示所有的url均会受到这个过滤器的影响。
现在我们就实现了我们自己的第一个filter。启动项目,然后运行一下,任何页面get请求,cmd参数中只要包含scrpit就会执行我们的代码:在实际应用中,就可通过代码逻辑,达到拦截恶意攻击的效果。
filter小结:访问web的时候,会根据xml中配置的url-pattern影响范围,执行对应的过滤器filter。
在firstFilter.java中的doFilter打上断点:运行程序,访问web,cmd参数只要不包含script就可以执行到下面的doFilter,比如访问:http://127.0.0.1:8080/Memory/?cmd=aaaa即可执行doFilter()。先看看参数request中有什么,如下图:
有一个属性filterChain,里面的分别是我们自己的filter,在容器解析web.xml时赋值。以及tomcat的默认filter(WsFilter 是 Tomcat 内部用于处理WebSocket 升级请求的一个过滤器。当客户端发起 WebSocket 连接请求时,这个过滤器会拦截该请求,并协助完成从 HTTP 协议到 WebSocket 协议的升级握手),启动时加入,暂不关注其具体细节。
把执行的流程往前推一推,看看到底是怎么一步步执行到我们自己的doFilter():
是一个变量filterChain的doFilter方法调用的-如下图:
而这里的 ApplicationFilterChain filterChain= ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); 是一个ApplicationFilterChain 类型,通过createFilterChain之后,得到的就是上图所示的,有个filters属性,里面就是当前应用中的filter ,但是是封装在ApplicationFilterConfig中的--它的主要作用是封装和管理filter过滤器的配置信息,在Servlet 容器启动时动态加载和初始化过滤器。
看跟进这个doFilter方法里面:如下图
里面只有一个逻辑,就是不管true或者false,都执行它的internalDoFilter()方法:
上图所示:在internalDoFilter()方法中, if (pos < n) { ApplicationFilterConfig filterConfig = filters[pos++]; try { Filter filter = filterConfig.getFilter();
判断是否数组是否越界,pos<n 从filters数组中获取一个ApplicationFilterConfig :如下图,pos和n初始为0,这里的n表示数组长度,0表示数组索引,调用之后自增(filters[pos++]):引用启动之后,n会根据读取到的filter数量,对应递增,这里为2。
然后获取到这个刚从数组中拿出来的filter,这里的filter就是我们的filter(firstFilter)。
然后再后面继续执行 filter.doFilter(request, response, this);执行到重写方法!
父类的doFilter方法:super.doFilter(request, response, chain);
里面再次调用doFilter():
再次跟进发现又回到了internalDoFilter()方法: