你写的单例一定安全吗?

简介: 前言:在并发环境中,我们可以用各种锁来保持单例的线程安全,当然这是从业务角度来考虑的;但是,从一个攻击者的角度来看,你的单例也许只保证了线程安全,当攻击者通过反射new出单例的实例时候(反射的可以改变你的私有构造函数)...

前言:

在并发环境中,我们可以用各种锁来保持单例的线程安全,当然这是从业务角度来考虑的;但是,从一个攻击者的角度来看,你的单例也许只保证了线程安全,当攻击者通过反射new出单例的实例时候(反射的可以改变你的私有构造函数),那攻击者就可以控制你的单例干很多坏事

举例:

单例代码:

/**
 * 懒汉式单例(简单点),不考虑多线程并发的模型
 */
public class MyInstance {
    private static MyInstance instance;

    private MyInstance(){

    }

    public static MyInstance getInstance(){
        if (instance == null){
            instance = new MyInstance();
        }
        return instance;
    }
}

攻击代码:

import java.lang.reflect.Constructor;

public class Main {

    public static void main(String[] args){

        System.out.println(MyInstance.getInstance());
        try
        {
            Class<MyInstance> classType = MyInstance.class;

            Constructor<MyInstance> constructor = classType.getDeclaredConstructor(null);
            constructor.setAccessible(true);//关键代码,将这个构造函数的权限设置为可进入的
            MyInstance myInstanceHack = constructor.newInstance();
            System.out.println(myInstanceHack);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

    }
}

攻击结果:

从结果来看,两个对象的地址是不一样的,那么攻击者就可以利用这个方式,干很多越权的事情。

如何防范呢?

其实,问题所在就是那个构造函数被攻击了,因此,我们只要保护好构造函数,就能基本防御这种攻击

方式一:

利用一个静态变量,保证构造函数只被掉一次

/**
 * 懒汉式单例(简单点),不考虑多线程并发的模型
 */
public class MyInstance {
    private static MyInstance instance;
    private static boolean isUsed;

    private MyInstance(){

        if (isUsed){
            //抛出异常
            throw new RuntimeException();
        }else {
            isUsed = true;
        }



    }

    public static MyInstance getInstance(){
        if (instance == null){
            instance = new MyInstance();
        }
        return instance;
    }
}

方式二:

利用枚举的特性:每个枚举量只会实例化一次,也就是枚举类的单例:

public enum MyInstanceEnum {
    INSTANCE;
    private MyInstance2 myInstance;

    MyInstanceEnum(){
        myInstance = new MyInstance2();
    }

    public MyInstance2 getMyInstance() {
        return myInstance;
    }
}

攻击时报错

总结:

当然,反射被设计出来,肯定不是用来攻击的,它的作用可以很多依赖注入的框架中,比如Spring各种框架,JSON解析等很多方面。(不晓得从哪里看到一句话:人的血液循环是复杂但是有规律的,打针(反射)在中间介入这个循环如果没摸清楚循环规律而乱戳表面看似达到效果,实际会引发其他问题)

目录
相关文章
|
7月前
|
API 开发者 Python
如何在API中实现搜索和过滤功能
本文介绍了如何为API添加搜索和过滤功能,使其更强大灵活。通过Flask示例,展示了按书名搜索、按作者或年份过滤书籍的方法,并结合两者实现复合查询。同时,提供了搜索不区分大小写、支持多过滤器组合、分页和输入验证等最佳实践。最后推荐了Apipost工具,它能简化API调试、负载测试及文档生成,提升开发效率。这些功能帮助用户更好地控制数据,优化API使用体验。
|
JavaScript 前端开发 小程序
基于springBoot + Vue电影售票系统分前后台【完整源码+数据库】
基于springBoot + Vue电影售票系统分前后台【完整源码+数据库】
271 4
|
安全 Java 测试技术
一文帮你搞定JDK8升级11
本文记录了作者从JDK8升级到11的实践过程和升级后的效果以及JDK11新玩法。
424 10
|
存储 分布式计算 大数据
阿里云大数据ACA及ACP复习题(361~370)
本人备考阿里云大数据考试时自行收集准备的题库,纯手工整理的,能够覆盖到今年7月份,应该是目前最新的,发成文章希望大家能一起学习,不要花冤枉钱去买题库背了,也希望大家能够顺利通关ACA和ACP考试(自己整理解析也需要时间,可能有更新不及时的情况哈)
|
缓存 安全 Java
使用 Mypy 检查 30 万行 Python 代码,总结出 3 大痛点与 6 个技巧!
使用 Mypy 检查 30 万行 Python 代码,总结出 3 大痛点与 6 个技巧!
304 2
|
SQL 运维 安全
客户说|享道出行 x DMS,构建一站式高效、安全的数据运维管理平台
享道出行通过引入一站式运维管理系统DMS,有效地降低数据运维的复杂性,提升运维团队的工作效率,同时保障IT系统的稳定性和安全性,最终实现业务的持续性和稳定发展。
|
供应链 算法
深度 | 5分钟读懂阿里零售通智慧供应链平台
大家好,先做个简单自我介绍,过去十年更多是在2B类业务方面做技术架构和研发工作,近两年专注在零售通供应链方面的技术架构和研发的工作。从技术视角分享二点最近几年感受比较深刻的,第一个点,从技术的架构的升级,从过去的电商架构到现在新零售的架构,比如从过去信息平台到交易平台再到现在供应链协同平台,其架构演进的核心动力是互联网、大数据等技术与商业不断融合和发展。
14280 0
|
机器学习/深度学习 传感器 算法
基于MUSIC算法实现DOA估计附MATLAB代码
基于MUSIC算法实现DOA估计附MATLAB代码
基于MUSIC算法实现DOA估计附MATLAB代码
|
消息中间件 Web App开发 移动开发
浏览器原理 37 # 任务调度:有了setTimeOut,为什么还要使用 requestAnimationFrame?
浏览器原理 37 # 任务调度:有了setTimeOut,为什么还要使用 requestAnimationFrame?
437 0
浏览器原理 37 # 任务调度:有了setTimeOut,为什么还要使用 requestAnimationFrame?