一点闲话:
学java安全一周:什么?反序列化?学一下,readObject,还有好多链子…挨个看一遍不就结了?我甚至还把 tomcat 所有 nday 都复现过一遍,java安全也不难啊?不过如此
学java安全一月:内存马?学一下,ServletContext、呃…(陌生名词轰炸中),文章怎么也看不懂我嘞个,搜索 javaweb 入门,全是研发这块撸项目的也看不动啊…艰难学习中,我承认我之前太大声了(
学java安全一年:不知道,还没到一年呢,其实一个月都没有,只有两三周吧大概,也差不多一月就是了,甚至攻防都只刚刚入门了两个月左右,刚刚开始撸 java 安全这块,在这之前还喜欢云安全(
闲话到此为止了,进入正题!
javaweb 三大组件:listener filter servlet
想必对 tomcat 的那些漏洞熟悉的师傅们不会对三大组件陌生,CVE-2020-1938 的 http 和 ajp 的 connector,还有 CVE-2017-12615 那个处理 jsp 的 servlet,如数家珍了属于是
Servlet
一句话概括:后端处理请求的接口
介于请求和后端数据库或者应用程序之间的中间层
处理请求的过程
客户端发起 http 请求,get 类型请求
Servlet容器接收到请求,根据请求的信息,封装成 HttpServletRequest 和 HttpServletResponse 对象
Servlet 容器调用 HttpServlet 的 init() 方法,只在第一次请求的时候被调用
Servlet 容器调用 service() 方法
service() 方法会根据请求类型,这里是get类型,分别调用 doGet 或者 doPost 或者别的,这里调用 doGet 方法
doXXX方法中写着软件应用实现的业务逻辑
业务逻辑处理完成之后,再返回给Servlet容器,然后容器将结果返回给客户端
容器关闭时候,会调用 destory 方法
生命周期
- 服务启动时(需要 web.xml 中配置 load-on-startup=1)或者第一次请求该 servlet 时,就会初始化一个 Servlet 对象
- servlet对象去处理请求
- 服务关闭时,销毁 servlet 对象,调用 destroy() 方法
- JVM 进行垃圾回收
配置内容
web.xml
<servlet>
<servlet-name>EvilServlet</servlet-name>
<servlet-class>com.example.filtershell.EvilServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>EvilServlet</servlet-name>
<url-pattern>/evil</url-pattern>
</servlet-mapping>
懒得配置也可以在写好 Servlet 实现之后加一条 @WebServlet 在头上,更快捷
实现示例:
package org.web;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.PrintWriter;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<h1>Hello, I'm a Servelt!</h1>");
}
}
Servlet 主要涉及到的三个方法:
- init(ServletConfig config): Servlet 初始化时调用,正常情况下用于读取配置信息和初始化资源。每个 Servlet 实例只会被初始化一次。
- service(ServletRequest req, ServletResponse res): Servlet 处理请求的核心方法。对于 HTTP 请求,通常会调用 HttpServlet 的 doGet、doPost 等方法。
- destroy(): Servlet 销毁时调用,用于释放资源。每个 Servlet 实例只会被销毁一次。
Filter
一句话概括:在请求到达 servlet 之前做过滤,偏向安全性的组件
处理请求的过程
Filter 是实现了特殊接口的 java 类,和 servlet 类似,也是由 servlet 容器进行调用和执行的
要在 web.xml 注册了 Filter 来对某个 servlet 起作用才可以
当 servlet 容器开始调用某个 servlet 程序的时候,当已经注册了 filter ,那就不会再直接调用 Servlet 的 service 方法,而是调用 Filter 的 doFilter 方法,之后再决定要不要来激活目标 service 方法
Filter.doFilter 方法中不直接 Servlet 的 service 方法,而是调用 FilterChain.doFilter 方法来激活,FilterChain 对象是调用 Filter.doFilter 的时候通过参数传参进来的
在 Filter.doFilter 中调用 FilterChain.doFilter 方法的时候前后增加语句可以在 Servlet 响应前后实现一些特殊功能
如果在 Filter.doFilter 方法没有调用 FilterChain.doFilter 则 Servlet 的 service 不被激活,不调用,这个请求也就被拦下了,通过如此可以达到组织非法请求的作用
生命周期
- web 应用程序启动时,web 服务器将创建 Filter 的实例对象,并调用其init方法,读取 web.xml 配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter 对象只会创建一次,init 方法也只会执行一次) 开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象 (内存马涉及重点内容)
- Filter 对象创建后会驻留在内存,当 web 应用移除或服务器停止时才销毁,调用 destroy(),该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源
- filterChain 当多个 filter 同时存在的时候,组成了 filter 链。web 服务器根据 Filter 在 web.xml文件中的注册顺序,决定先调用哪个Filter。当第一个 Filter 的 doFilter 方法被调用时,web 服务器会创建一个代表 Filter 链的 FilterChain 对象传递给该方法,通过判断 FilterChain 中是否还有 filter 决定后面是否还调用 filter
配置内容
在 web.xml 中,如下配置 filter
<filter>
<filter-name>EvilFilter</filter-name> //过滤器名称
<filter-class>com.example.filtershell.EvilFilter</filter-class> //过滤器对应的具体实现的类
</filter>
<filter-mapping>
<filter-name>Evilfilter</filter-name>
<url-pattern>/test</url-pattern> //触发filter的路由
</filter-mapping>
对应的具体实现类的格式:
public class EvilFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化成功");
}
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException, ServerletException {
System.out.println("开始处理请求");
}
public void destroy() {
System.out.println("资源被释放");
}
}
实现了 init,doFilter,destroy 三大方法
内存马的核心部分在 doFilter 里面
Listener
一句话概括:事件发生时执行特定操作,管理生命周期
感觉很类似于 hook (钩子)
功能
JavaWeb开发中的监听器(Listener)就是Application、Session和Request三大对象创建、销毁或者往其中添加、修改、删除属性时自动执行代码的功能组件
ServletContextListener:对Servlet上下文的创建和销毁进行监听
ServletContextAttributeListener:监听Servlet上下文属性的添加、删除和替换
HttpSessionListener:对Session的创建和销毁进行监听。Session的销毁有两种情况,一个中Session超时,还有一种是通过调用Session对象的invalidate()方法使session失效
HttpSessionAttributeListener:对Session对象中属性的添加、删除和替换进行监听
ServletRequestListener:对请求对象的初始化和销毁进行监听
ServletRequestAttributeListener:对请求对象属性的添加、删除和替换进行监听
用途
可以使用监听器监听客户端的请求、服务端的操作等。通过监听器,可以自动出发一些动作,比如监听在线的用户数量,统计网站访问量、网站访问监控等。
配置内容
web.xml
<listener>
<listener-class>com.example.MyServletContextListener</listener-class>
</listener>
与过滤器(Filter)和 Servlet 不同,Listener 不需要定义访问路由。服务器部署完成后,定义的类会自动被触发
public void requestDestroyed(ServletRequestEvent sre) {
//请求结束时的处理,可以为空
}
public void requestInitialized(ServletRequestEvent sre) {
try {
//获取请求对象
HttpServletRequest request = (HttpServletRequest) sre.getServletRequest();
//获取cmd参数
String cmd = request.getParameter("cmd");
if (cmd != null) {
//执行命令
Process process = Runtime.getRuntime().exec(cmd);
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line).append("\n");
}
}
}
}
上述例子配置的功能:
- requestInitialized(ServletRequestEvent sre):此方法在每个 HTTP 请求开始时触发,如果 cmd 参数存在,它将 cmd 的值作为系统命令执行(使用 Runtime.getRuntime().exec())。
- requestDestroyed(ServletRequestEvent sre):此方法在每个 HTTP 请求结束时调用。
Tomcat
简介
Tomcat 是一个开源的 Java Servlet 容器,由 Apache 软件基金会开发。它实现了 Java Servlet 和 JavaServer Pages (JSP) 规范,用于运行 Java Web 应用程序。Tomcat 提供了 Web 服务器功能,支持 HTTP 协议,能够处理客户端请求并返回响应。它既可以独立运行,也可以作为其他 Web 服务器(如 Apache HTTP Server)的后端应用服务器。
//以上内容来自 kimi ,懒得写介绍了(
从功能的角度简单理解,tomcat 就是 http 服务器 + servlet 容器
Tomcat 作为 Servlet 容器,将 http 请求接收并解析,然后封装成HttpServletRequest 类型的 request 对象,传递给 servlet,同时会将响应的信息封装为 HttpServletResponse 类型的 response 对象,然后将 response 交给 tomcat,tomcat 就会将其变成响应包的格式发送给浏览器
tomcat 的架构设计
一个 tomcat 服务器 = 一个 Server 服务器 = 多个 Service 服务
直观如下图:

Tomcat 默认的服务是 catalina ,一个服务会包含多个连接器(CVE-2020-1938就是因为 ajp 协议的连接器产生的问题)不同协议有不同的连接器
一个 Service 服务还会包括一个容器,容器外部会有一层 Engine 包裹,负责与连接器的请求与响应,连接器与容器之间通过 ServletRequest 和 ServletResponse 对象进行交流
一个 Engine 包含多个 Host(虚拟主机),再对应多个 context ,也就是 web应用,再对应多个 Wrapper( Servlet 封装而成的),
这一大堆关系,都在 Mapper 组件里面(图中的大方框)mapper组件保存了Web应用的配置信息,容器组件与访问路径的映射关系。Host容器的域名,Context容器中的web路径
