Java Web安全之Java Web基础-Servlet

Java Web安全之Java Web基础

Servlet

Java EE指的是Java平台企业版(Java Platform Enterprise Edition),之前称为Java 2 Platform, Enterprise Edition(J2EE),2017 年的 9 月Oracle将Java EE 捐赠给 Eclipse 基金会,由于Oracle持有Java商标原因,Eclipse基金于2018年3月将Java EE更名为Jakarta EE

Java EE和Servlet版本

Java EE历史版本

Java SE/JDK版本Java EE版本Servlet版本发布时间
//Servlet 1.0(1997年6月)
JDK1.1/Servlet 2.0/
//Servlet 2.1(1998年11月)
JDK1.2J2EE 1.2Servlet 2.2(1999年12月12日)
JDK1.2J2EE 1.3Servlet 2.3(2001年9月24日)
JDK1.3J2EE 1.4Servlet 2.4(2003年11月11日)
JDK1.5Java EE 5Servlet 2.5(2006年5月11日)
JDK1.6Java EE 6Servlet 3.0(2009年12月10日)
/Java EE 7Servlet 3.1(2013年5月28日)
/Java EE 8Servlet 4.0(2017年8月31日)
/Jakarta EE8Servlet 4.0(2019年8月26日)

由上表可知Java EE并不是Java SE的一部分(JDK不自带),Java EE的版本也不完全是对应了JDK版本,我们通常最为关注的是Java EE对应的Servlet版本。不同的Servlet版本有着不一样的特性,Servlet容器(如GlassFish/Tomcat/Jboss)也会限制部署的Servlet版本。Java流行的Spring MVC(基于Servlet机制实现)、Struts2(基于Filter机制实现)等Web框架也是基于不同的Java EE版本封装了各自的框架。

Servlet 3.0 规范Servlet 3.1 规范Servlet 4.0 规范

Tomcat Servlet版本

Tomcat版本Java EE版本Servlet版本JSP版本发布时间
Tomcat 5.0.0 +J2EE 1.4Servlet 2.4JSP 2.0(2003年11月24日)
Tomcat 6.0.0 – Tomcat 6.0.44Java EE 5Servlet 2.5JSP 2.1(2006年5月11日 – 2007年9月11日)
Tomcat 7.0.0 – Tomcat 7.0.25Java EE 6Servlet 3.0JSP 2.2(2009年12月10日 – 2011年2月6日)
Tomcat 8.0.0 +Java EE 7Servlet 3.1JSP 2.3(2013年5月28日)
Tomcat 9.0.0 +Java EE 8Servlet 4.0JSP 2.3(2017年2月5日)
Tomcat 10.0.0 +Jakarta EE8Servlet 4.0JSP 2.3/

参考: Web Application Specifications

Servlet是在 Java Web容器中运行的小程序,通常我们用Servlet来处理一些较为复杂的服务器端的业务逻辑。ServletJava EE的核心,也是所有的MVC框架的实现的根本!

基于Web.xml配置

Servlet3.0 之前的版本都需要在web.xml 中配置servlet标签servlet标签是由servletservlet-mapping标签组成的,两者之间通过在servletservlet-mapping标签中同样的servlet-name名称来实现关联的。

Servlet的定义

定义一个 Servlet 很简单,只需要继承javax.servlet.http.HttpServlet类并重写doXXX(如doGet、doPost)方法或者service方法就可以了,其中需要注意的是重写HttpServlet类的service方法可以获取到上述七种Http请求方法的请求。

javax.servlet.http.HttpServlet:

在写Servlet之前我们先了解下HttpServlet,javax.servlet.http.HttpServlet类继承于javax.servlet.GenericServlet,而GenericServlet又实现了javax.servlet.Servletjavax.servlet.ServletConfigjavax.servlet.Servlet接口中只定义了servlet基础生命周期方法:init(初始化)getServletConfig(配置)service(服务)destroy(销毁),而HttpServlet不仅实现了servlet的生命周期并通过封装service方法抽象出了doGet/doPost/doDelete/doHead/doPut/doOptions/doTrace方法用于处理来自客户端的不一样的请求方式,我们的Servlet只需要重写其中的请求方法或者重写service方法即可实现servlet请求处理。

javax.servlet.http.HttpServlet类:

Java Web安全之Java Web基础-Servlet

TestServlet示例代码:

package com.anbai.sec.servlet;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * Creator: yz
 * Date: 2019/12/14
 */
// 如果使用注解方式请取消@WebServlet注释并注释掉web.xml中TestServlet相关配置
//@WebServlet(name = "TestServlet", urlPatterns = {"/TestServlet"})
public class TestServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        doPost(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter out = response.getWriter();
        out.println("Hello World~");
        out.flush();
        out.close();
    }

}

请求TestServlet示例:

Java Web安全之Java Web基础-Servlet

Servlet Web.xml配置

定义好了Servlet类以后我们需要在web.xml中配置servlet标签才能生效。

基于配置实现的Servlet:

Java Web安全之Java Web基础-Servlet

Servlet 3.0 基于注解方式配置

基于注解的Servlet:

值得注意的是在 Servlet 3.0 之后( Tomcat7+)可以使用注解方式配置 Servlet 了,在任意的Java类添加javax.servlet.annotation.WebServlet注解即可。

基于注解的方式配置Servlet实质上是对基于web.xml方式配置的简化,极大的简化了Servlet的配置方式,但是也提升了对Servlet配置管理的难度,因为我们不得不去查找所有包含了@WebServlet注解的类来寻找Servlet的定义,而不再只是查看web.xml中的servlet标签配置。

Java Web安全之Java Web基础-Servlet

Servlet 3.0 特性

  1. 新增动态注册ServletFilter 和Listener的API(addServletaddFilteraddListener)。
  2. 新增@WebServlet@WebFilter@WebInitParam@WebListener@MultipartConfig注解。
  3. 文件上传支持,request.getParts()
  4. 非阻塞 IO,添加异步 IO
  5. 可插拔性(web-fragment.xmlServletContainerInitializer)。

Request & Response

B/S架构中最重要的就是浏览器和服务器端交互,Java EE将其封装为请求响应对象,即 request(HttpServletRequest) 和 response(HttpServletResponse)

HttpServletRequest对象用于处理来自客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP 中的所有信息都封装在这个对象中,通过HttpServletRequest对象可以获取到客户端请求的所有信息。

HttpServletResponse对象用于响应客户端的请求,通过HttpServletResponse对象可以处理服务器端对客户端请求响应。

HttpServletRequest常用方法

方法说明
getParameter(String name)获取请求中的参数,该参数是由name指定的
getParameterValues(String name)返回请求中的参数值,该参数值是由name指定的
getRealPath(String path)获取Web资源目录
getAttribute(String name)返回name指定的属性值
getAttributeNames()返回当前请求的所有属性的名字集合
getCookies()返回客户端发送的Cookie
getSession()获取session回话对象
getInputStream()获取请求主题的输入流
getReader()获取请求主体的数据流
getMethod()获取发送请求的方式,如GET、POST
getParameterNames()获取请求中所有参数的名称
getRemoteAddr()获取客户端的IP地址
getRemoteHost()获取客户端名称
getServerPath()获取请求的文件的路径

HttpServletResponse常用方法

方法说明
getWriter()获取响应打印流对象
getOutputStream()获取响应流对象
addCookie(Cookie cookie)将指定的Cookie加入到当前的响应中
addHeader(String name,String value)将指定的名字和值加入到响应的头信息中
sendError(int sc)使用指定状态码发送一个错误到客户端
sendRedirect(String location)发送一个临时的响应到客户端
setDateHeader(String name,long date)将给出的名字和日期设置响应的头部
setHeader(String name,String value)将给出的名字和值设置响应的头部
setStatus(int sc)给当前响应设置状态码
setContentType(String ContentType)设置响应的MIME类型

JSP基础

JSP(JavaServer Pages) 是与 PHPASPASP.NET 等类似的脚本语言,JSP是为了简化Servlet的处理流程而出现的替代品,早期的Java EE因为只能使用Servlet来处理客户端请求而显得非常的繁琐和不便,使用JSP可以快速的完成后端逻辑请求。

正因为在JSP中可以直接调用Java代码来实现后端逻辑的这一特性,黑客通常会编写带有恶意攻击的JSP文件(俗称WebShell)来实现对服务器资源的恶意请求和控制。

现代的MVC框架(如:Spring MVC 5.x)已经完全抛弃了JSP技术,采用了模板引擎(如:Freemark)或者RESTful的方式来实现与客户端的交互工作,或许某一天JSP技术也将会随着产品研发的迭代而彻底消失。

JSP 三大指令

  1. <%@ page ... %> 定义网页依赖属性,比如脚本语言、error页面、缓存需求等等
  2. <%@ include ... %> 包含其他文件(静态包含)
  3. <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 引入标签库的定义

JSP 表达式(EL)

EL表达式(Expression Language)语言,常用于在jsp页面中获取请求中的值,如获取在Servlet中设置的Attribute:${名称}。使用EL表达式可以实现命令执行,我们将会在后续EL表达式章节中详细讲解。

JSP 标准标签库(JSTL)

JSP标准标签库(JSTL)是一个JSP标签集合,它封装了JSP应用的通用核心功能。

JSTL支持通用的、结构化的任务,比如迭代,条件判断,XML文档操作,国际化标签,SQL标签。 除了这些,它还提供了一个框架来使用集成JSTL的自定义标签。

JSP 九大对象

从本质上说 JSP 就是一个Servlet,JSP 引擎在调用 JSP 对应的 jspServlet 时,会传递或创建 9 个与 web 开发相关的对象供 jspServlet 使用。 JSP 技术的设计者为便于开发人员在编写 JSP 页面时获得这些 web 对象的引用,特意定义了 9 个相应的变量,开发人员在JSP页面中通过这些变量就可以快速获得这 9 大对象的引用。

如下:

变量名类型作用
pageContextPageContext当前页面共享数据,还可以获取其他8个内置对象
requestHttpServletRequest客户端请求对象,包含了所有客户端请求信息
sessionHttpSession请求会话
applicationServletContext全局对象,所有用户间共享数据
responseHttpServletResponse响应对象,主要用于服务器端设置响应信息
pageObject当前Servlet对象,this
outJspWriter输出对象,数据输出到页面上
configServletConfigServlet的配置对象
exceptionThrowable异常对象

JSP、Servlet之间的关系

JSP、JSPX 文件是可以直接被 Java 容器直接解析的动态脚本, jsp 和其他脚本语言无异,不但可以用于页面数据展示,也可以用来处理后端业务逻辑。

从本质上说 JSP 就是一个Servlet ,因为 jsp 文件最终会被编译成 class 文件,而这个 class 文件实际上就是一个特殊的Servlet 。

JSP文件会被编译成一个java类文件,如index.jsp在Tomcat中Jasper编译后会生成index_jsp.javaindex_jsp.class两个文件。而index_jsp.java 继承于HttpJspBase类,HttpJspBase是一个实现了HttpJspPage接口并继承了HttpServlet的标准的Servlet__jspService方法其实是HttpJspPage接口方法,类似于Servlet中的service方法,这里的__jspService方法其实就是HttpJspBaseservice方法调用。

Java Web安全之Java Web基础

Filter

javax.servlet.FilterServlet2.3新增的一个特性,主要用于过滤URL请求,通过Filter我们可以实现URL请求资源权限验证、用户登陆检测等功能。

Filter是一个接口,实现一个Filter只需要重写initdoFilterdestroy方法即可,其中过滤逻辑都在doFilter方法中实现。

Filter的配置类似于Servlet,由<filter><filter-mapping>两组标签组成,如果Servlet版本大于3.0同样可以使用注解的方式配置Filter。

基于注解实现的Filter示例:

Java Web安全之Java Web基础

Filter和Servlet的总结

对于基于FilterServlet实现的简单架构项目,代码审计的重心集中于找出所有的Filter分析其过滤规则,找出是否有做全局的安全过滤、敏感的URL地址是否有做权限校验并尝试绕过Filter过滤。第二点则是找出所有的Servlet,分析Servlet的业务是否存在安全问题,如果存在安全问题是否可以利用?是否有权限访问?利用时是否被Filter过滤等问题,切勿看到ServletJSP中的漏洞点就妄下定论,不要忘了Servlet前面很有可能存在一个全局安全过滤的Filter

FilterServlet都是Java Web提供的API,简单的总结了下有如下共同点。

  1. FilterServlet都需要在web.xml注解(@WebFilter@WebServlet)中配置,而且配置方式是非常的相似的。
  2. FilterServlet都可以处理来自Http请求的请求,两者都有requestresponse对象。
  3. FilterServlet基础概念不一样,Servlet定义是容器端小程序,用于直接处理后端业务逻辑,而Filter的思想则是实现对Java Web请求资源的拦截过滤。
  4. FilterServlet虽然概念上不太一样,但都可以处理Http请求,都可以用来实现MVC控制器(Struts2Spring框架分别基于FilterServlet技术实现的)。
  5. 一般来说Filter通常配置在MVCServletJSP请求前面,常用于后端权限控制、统一的Http请求参数过滤(统一的XSSSQL注入Struts2命令执行等攻击检测处理)处理,其核心主要体现在请求过滤上,而Servlet更多的是用来处理后端业务请求上。

Cookie 和 Session 对象

Cookie 是最常用的Http会话跟踪机制,且所有Servlet容器都应该支持。当客户端不接受Cookie时,服务端可使用URL重写的方式作为会话跟踪方式。会话ID必须被编码为URL字符串中的一个路径参数,参数的名字必须是 jsessionid

浏览器和服务端创建会话(Session)后,服务端将生成一个唯一的会话ID(sessionid)用于标识用户身份,然后会将这个会话ID通过Cookie的形式返回给浏览器,浏览器接受到Cookie后会在每次请求后端服务的时候带上服务端设置Cookie值,服务端通过读取浏览器的Cookie信息就可以获取到用于标识用户身份的会话ID,从而实现会话跟踪和用户身份识别。

因为Cookie中存储了用户身份信息,并且还存储于浏览器端,攻击者可以使用XSS漏洞获取到Cookie信息并盗取用户身份就行一些恶意的操作。

from

转载请注明出处及链接

Leave a Reply

您的电子邮箱地址不会被公开。 必填项已用 * 标注