Java 安全研究基础

JavaEE

搞过 Java Web 开发的多少会知道 JavaEE。从字面上来看,它的意思是 Java Platform, Enterprise Edition,即 Java 平台企业版。实际上 Java EE 包含了一系列标准,这些标准主要用来为企业级应用提供分布式计算、Web 服务等的统一定义和接口。

Servlet

从实现上来说,Servlet 是一个实现了特定接口的 Web 组件,由 Servlet 容器去加载并运行。容器本身并不一定是 Web 服务器,但容器需要至少支持 HTTP 请求,并将请求的内容封装成 Servlet 接口的参数;目前常见的 Servlet 容器有如 Tomcat、GlassFish、JBoss 等
基础的接口如下

package javax.servlet;
import java.io.IOException;

public interface Servlet {
    void init(ServletConfig var1) throws ServletException;
    ServletConfig getServletConfig();
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    String getServletInfo();
    void destroy();
}

service 方法,用于处理客户端的请求,并填充所需的返回结果。init 和 destory 是生命周期方法,在 Servlet 被加载和销毁时只执行一次。 getServletConfig 返回的对象是类似 name/value 格式的配置信息,由用户配置文件中进行传入。

基础使用

import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;

@WebServlet(name = "helloServlet", value = "/hello")
public class HelloServlet extends HttpServlet {
    private String message;

    public void init() {
        message = "Hello World!";
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html");

        // Hello
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>" + message + "</h1>");
        out.println("</body></html>");
    }

    public void destroy() {
    }
}

正常情况下定义好 HelloServlet 类之后,容器并不知道这个类的存在,也不知道应该将什么 HTTP 路径映射到这个 Servlet,传统上需要用户自己修改 web.xml 配置文件(这也是 Servlet 标准的一部分),添加 <servlet><servlet-mapping> 标签来指定这些信息。在 Servlet 3.0 之后,就可以使用注解的方式配置 Servlet 了,如上面的 WebServlet 注解。

Filter

用于为所有 Servlet 添加全局性的鉴权和过滤,一个简单的示例如下:

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.io.PrintWriter;

@WebFilter(filterName = "HelloFilter", urlPatterns = {"/*"})
public class HelloFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        String passwd = servletRequest.getParameter("passwd");
        if (passwd != null && passwd.equals("123456")) {
            filterChain.doFilter(servletRequest, servletResponse);
        } else {
            try (PrintWriter out = servletResponse.getWriter()) {
                out.println("passwd error!");
            }
        }
    }

    @Override
    public void destroy() {

    }
}

JSP

JSP 的全称是 Java Server Pages,包含一系列技术的组合,既表示动态网页的框架,也表示一种文件类型。JSP 的标准为 JSR 245,其中包含两个主要文档,分别是 JSP 的标准文档和 JSP EL 表达式的标准。

我们可以用 Servlet 来生成网页中的动态内容。但存在一个问题,就是即使我们只做出微小的修改,也需要对源码进行重新编译和部署。JSP 解决了 Servlet 的这些问题,它是 Servlet 很好的补充,可以专门用作为用户呈现视图(View),而 Servlet 作为控制器(Controller)专门负责处理用户请求并转发或重定向到某个页面。

底层原理

SP 作为一个脚本引擎,其本质上也是通过 Servlet 实现的,即 JSP 会被容器编译为对应的 Servlet 并加载。该类在第一次访问的时候才会生成、编译、加载,因此对于没访问过的页面是没有对应文件的。另外,在 jspService 方法中为了方便 JSP 的代码编写,定义了几个常用的对象。

request,对应客户端请求的 HttpServletRequest 对象;
response,对应返回的 HttpServletResponse 对象;
session,对应 HttpSession,存储当前请求会话信息;
application,对应 ServletContext,用于全局共享数据;
config,对应 ServletConfig 即 Servlet 的配置对象;
page,为 Object 类型,指向当前 Servlet 对象;
out,即控制页面输出的 JspWriter;
pageContext,当前页面共享数据对象;
这几个对象通常称为 JSP 的 “八大对象”,或者 “九大对象” (算上 exception 对象)。

Java 表达式语言

上节中我们说到 JSP 的标准中包含了两份文档,一份是 JSP 语言本身的标准,另一份则是表达式语言的标准。Java 表达式语言即 Expression Language,简称为 EL,虽然 JSP 中可以写 Java 代码,但这一方面对不懂 Java 语言的人不太友好,另一方面很多时候我们仅需要在 JSP 中执行比较、循环、加减乘除等简单操作,而引入完整的 Java 引擎显得有点 overkill 了。

public class ELTestServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws ServletException, IOException {
        
        List list = new ArrayList<>();
        list.add(Double.valueOf(1.0));
        list.add(Double.valueOf(2.0));
        list.add(Double.valueOf(3.0));

        ELProcessor elp = new ELProcessor();
        elp.defineBean("data", list);    

        Object message = (Double)elp.eval(
            "n = data.stream().count(); s = data.stream().sum(); sq = data.stream().map(i -> i*i).sum(); Math.sqrt(sq/n - Math.pow(s/n, 2))");
        res.getWriter().println(message);

        message = (Double)elp.eval(
            "n = 0; s = 0; sq = 0; data.stream().forEach(d -> (n = n + 1; s = s + d; sq = sq + d*d)); Math.sqrt(sq/n - Math.pow(s/n, 2))");
        res.getWriter().println(message);

        message = (Double)elp.eval(
            "n = 0; s = 0; sq = data.stream().reduce(0, (a, i) -> (n = n + 1; s = s + i; a + i*i)); Math.sqrt(sq/n - Math.pow(s/n, 2))"); 
        res.getWriter().println(message);
    }
}

这类表达式语言通常作为模版语言在 Web 页面中使用,如果其中的表达式能够被恶意输入所控制,就可能出现任意代码执行的危害。历史上出现过许多由于 EL 表达式注入导致的远程代码执行,因此这算是 Java 中高危的攻击面之一

JDBC

在传统 MVC 架构中,将内容呈现的过程分成模型(Model),视图(View),控制器(Controller)三个部分,现在我们已经介绍了控制器部分的 Servlet,视图层的 JSP,为了使得一个网页能够真正有价值,还缺少其中关键的模型部分,即数据来源。
JDBC (Java Database Connectivity) 就是 Java 访问数据库的一个标准。

使用方法

 import java.sql.*;

 public class TestJdbc {
   public static void main(String[] args) throws Exception {
     // Class.forName("com.mysql.jdbc.Driver");

     // 1. Establishing the connection with the database
     Connection conn = DriverManager.getConnection(
       "jdbc:mysql://localhost:3306/mydb?useSSL=false", "root", "rxhtemp");

     // 2. Creating a statement object to execute a query
     Statement stmt = conn.createStatement();

     // 3. Executing the query and getting the ResultSet
     ResultSet rs = stmt.executeQuery("SELECT * FROM user");

     // 4. Processing the ResultSet
     while (rs.next()) {
       int id = rs.getInt("id");
       String name = rs.getString("name");
       System.out.println("ID: " + id + ", Name: " + name);
     }

     // 5. Closing the database resources
     rs.close();
     stmt.close();
     conn.close();
   }
 }

JMX

JMX 全称为 Java Management Extensions,即 Java 管理拓展,主要用于管理和监控 Java 程序。

组成部分

MBean:

全称是 Managed Bean,类似于 Java Bean,主要用来进行消息传递。与 Java Bean 的区别是 JMX 中对这些 Bean 进行了额外的定义,兼容 JMX 标准的 MBean 才可以被加载。
MBeanServer,主要用于 MBean 的管理,同名接口定义在 javax.management 中,包含 MBean 创建、注册以及删除等操作相关的接口。

Connector/Adaptor:

这是直接面对客户端的组件,负责具体协议的连接或者转换。在 JMX 标准中提到,RMI Connector 是在 JMX 标准实现中唯一强制实现的协议。RMI (Remove Method Invocation) 是 Java 中的远程调用接口,其具体的传输协议可以不同,常见有以下两种:

JRMP (Java Remote Method Protocol),这是 RMI 的默认传输协议,专为 Java 而开发,因此性能较好;
IIOP (Internet Inter-ORB Protocol),CORBA 标准的一部分,依赖于 CORBA 类库,因此可以实现多语言传输 RMI 远程对象,更适用于跨平台开发;

Adaptor :

称为适配器,和连接器类似,主要将客户端对服务器中 MBean 的操作适配为其他协议,比如 SNMP 或者 HTTP 等。例如,Jolokia 就是一个常用的开源 JMX-HTTP 适配器,可以使远程的 JMX 客户端通过 HTTP/HTTPS 协议访问 JMX MBean。

使用示例

JMX 要求 MBean 首先需要是一个接口,且接口名字以 MBean 结尾,例如:

public interface DemoMBean {
    String getName();
    String getPassword();
    void say(String what);
}

一个具体的 MBean 需要实现上述接口:

class Demo implements DemoMBean {
    @Override
    public String getName() { return "evilpan"; }
    @Override
    public String getPassword() { return "jmxdemo"; }
    @Override
    public void say(String what) {
        System.out.println(getName() + " say: " + what);
    }
}

为了能够被客户端访问,需要将该 MBean 的实例注册到 MBeanServer:

MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName userName = new ObjectName("com.evilpan:type=Foo,name=bar");
server.registerMBean(new Demo(), userName);

客户端连接设置

MBeanServer server = ManagementFactory.getPlatformMBeanServer();
// Register MBean ...
LocateRegistry.createRegistry(7890);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://localhost:7890/jmxrmi");
JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, server);
jcs.start();

参考链接

https://evilpan.com/2023/04/01/java-ee/

posted @   Ho1d_F0rward  阅读(11)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示