在Spring MVC中,Interceptor和Controller都可以被用来实现内存马,但攻击者通常优先选择Interceptor。其可以在请求到达Controller之前和之后执行代码。它能够拦截多个Controller的请求。而Controller是处理特定请求的处理器,它只对映射到其上的请求路径起作用。这跟tomcat的中的内存马filter(过滤器)与servlet的执行过程几乎差不多,而Interceptor是spring中的拦截器。
单在spring中我们一般会优先选用Interceptor作为内存马,而不是controller,其主要原因如下:
隐蔽性对比:
Interceptor:由于Interceptor不直接与特定的URL映射关联,它通常不会在路由表中暴露,因此更难被扫描和检测。 Controller:Controller通常通过注解(如@RequestMapping)与特定URL映射关联,这些映射信息在Spring中是可查询的,因此更容易被安全扫描工具发现。
动态注册技术对比:
Interceptor:通过实现WebMvcConfigurer接口,可以动态添加Interceptor,而且可以全局生效。动态注册一个Interceptor相对简单,且不需要破坏现有的Controller结构。其通常不依赖具体的业务逻辑,可以独立于业务代码存在。 Controller:动态注册Controller需要修改Spring MVC的路由映射,更复杂,需要定义具体的处理逻辑,而且容易与现有路由冲突,引起异常。
触发阶段:
Interceptor:可以在请求处理的前后多个阶段进行控制,类似tomcat的filter,比如在预处理、后处理和完成后的处理,因此可以更灵活地操纵请求和响应。其可在controller之前执行。 Controller:只能在映射的请求到达时执行,并且需要处理具体的业务逻辑。
我们优先选择Interceptor作为内存马主要是因为:
隐蔽性优先 - 不新增路由端点,避免安全扫描
控制范围广 - 单一注入点覆盖所有请求路径
技术实现简洁 - 标准接口实现,注册机制稳定可靠
系统影响小 - 异常情况下对业务影响可控
检测难度高 - 在框架层面运作,传统安全设备难以识别
Interceptor(拦截器) :是 Spring MVC 框架中的一种核心组件,一种用于在请求处理过程中进行拦截和处理的机制,实现对 HTTP 请求的拦截和处理,从而实现对 Web 请求的精细化控制和统一处理。
这句话看起来似曾相识,是的,这个能力与java中的filter非常相像,而spring中的controller与Java servlet也非常相似,再次证明了上一个文章说的spring其实是运行在tomcat之上的框架。Interceptor 与 Filter 高度相似,都专注于对“请求-响应”流程的干预和控制-对比如下:
| 维度 | Interceptor (Spring MVC) | Filter (Java Servlet) |
| 核心目标 | 干预请求处理过程 | 干预请求处理过程 |
| 执行时机 | 在 Controller之前和之后执行 | 在 Servlet之前和之后执行(包裹了Interceptor) |
| 工作模式 | “拦截”和“处理” | “过滤”和“处理” |
| 控制能力 | 可以阻止请求继续传递(preHandle返回false) | 可以阻止请求继续传递(FilterChain.doFilter) |
| 应用场景 | 权限检查、日志记录、性能监控、通用数据处理 | 字符编码设置、安全过滤、压缩响应、XSS防御 |
实现一个有效的 Spring Interceptor 需要满足以下几个关键要求:
1.Interceptor类必须实现 HandlerInterceptor 接口,重写preHandle()方法。
2.需要通过一个配置类显式将Interceptor类注册到拦截器链中
3.配置类需要实现WebMvcConfigurer接口,重写addInterceptor()方法。
现在依次实现:
实现1.首先新建一个TestInterceptor.java类,代码如下:
package com.example.demo.demos.web;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("Interceptor执行");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
}
实现2and3.新建一个配置类InterceptorConf.java,用于注册TestInterceptor。
package com.example.demo.demos.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class InterceptorConf implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");//设置所有页面经过过滤器
}
}
完成,启动看以下效果:因为设置所有页面addPathPatterns("/*")均通过过滤器,启动之后访问http://127.0.0.1:8080/a 任意页面即可看到过滤器执行了!

回到TestInterceptor.java类文件,看看其重写的方法各个参数:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 有requet,response,handler,都是熟悉的味道!
既然有requet,response那么首先想到的就是直接可以在这里通过requet的参数执行命令,通过response返回命令结果。那么Interceptor就可以通过该方法达到对用户的数据进行校验,过滤的目的了---高度相似filter。
实验下,修改以下TestInterceptor代码如下:做一个简单的XSS拦截器
package com.example.demo.demos.web;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestInterceptor implements HandlerInterceptor {//实现接口
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String cmd = request.getParameter("cmd");//请求参数cmd
if (cmd.contains("script")){//过滤条件
res