Finalizer (终结器)Attack

简介

在构造函数中抛出异常,目的是创建对象失败时当对象抛出异常后,对象会被垃圾清理器回收。而当垃圾清理器准备释放对象的内存时,它首先调用finalize方法,然后再回收内存。Finalizer攻击就是重载finalize方法,在这个方法内保存这个对象。这个对象是部分初始化对象,它应该被清除,但是没有,导致它已经初始化的信息泄漏,并且获取对象后可以调用其方法,如果对象包含一些操作敏感信息的方法,就会被不法分子利用。

例子:

public class SensitiveOperation {

    public SensitiveOperation(){
        if(!doSecurityCheck()){
            throw new SecurityException("Security check failed!");
        }
    }

    //Security check return false
    private boolean doSecurityCheck(){
        return false;
    }

    public void storeMoney(){
        System.out.println("Store 1000000 RMB!");
    }
}

上面的例子中,我们在构造函数中做了一个securityCheck,因为这个securityCheck返回的值是false,所以会抛出SecurityException。

调用:

public static void main(String[] args) {
        SensitiveOperation sensitiveOperation = new SensitiveOperation();
        sensitiveOperation.storeMoney();
}

异常:

Exception in thread "main" java.lang.SecurityException: Security check failed!
    at com.flydean.SensitiveOperation.<init>(SensitiveOperation.java:11)
    at com.flydean.SensitiveUsage.main(SensitiveUsage.java:10)

Finalizer Attack

上面类我们 new 时就抛出异常,对象会被回收,首先调用 finalizer 方法。因此可以继承重写 finalizer 方法去偷偷做操作。

攻击例子:

public class SensitiveOperationFinalizer extends  SensitiveOperation{

    public SensitiveOperationFinalizer(){
    }

    @Override
    protected void finalize() {
        System.out.println("We can still do store Money action!");
        this.storeMoney();
        System.exit(0);
    }
}

上的例子中,我们继承了SensitiveOperation,并且实现了finalize方法,在finalize中,我们调用了storeMoney。看下运行的代码: 

   public void testFinalizer() throws InterruptedException {
        try {
        SensitiveOperation sensitiveOperation = new SensitiveOperationFinalizer();
            sensitiveOperation.storeMoney();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        System.gc();
        Thread.sleep(10000);
    }

 运行结果:

Security check failed!
We can still do store Money action!
Store 1000000 RMB!

可以看到,虽然我们构造函数抛出了异常,但是storeMoney的操作还是被执行了! 

解决Finalizer Attack

使用final class

如果使用final class,那么类是不能够被继承的,问题自然就解决了。

public final class SensitiveOperationFinal {

    public SensitiveOperationFinal(){
        if(!doSecurityCheck()){
            throw new SecurityException("Security check failed!");
        }
    }

    //Security check return false
    private boolean doSecurityCheck(){
        return false;
    }

    public void storeMoney(){
        System.out.println("Store 1000000 RMB!");
    }
}

使用final finalize方法 

因为子类想要重写finalize方法,如果我们的父类中finalize方法定义为final,也可以解决这个问题。

public final class SensitiveOperationFinal {

    public SensitiveOperationFinal(){
        if(!doSecurityCheck()){
            throw new SecurityException("Security check failed!");
        }
    }

    //Security check return false
    private boolean doSecurityCheck(){
        return false;
    }

    public void storeMoney(){
        System.out.println("Store 1000000 RMB!");
    }
    
    final protected void finalize() {
    }
}

使用flag变量

我们可以在对象构建完毕的时候设置一个flag变量,然后在每次安全操作的时候都去判断一下这个flag变量,这样也可以避免之前提到的问题:

public class SensitiveOperationFlag {

    private volatile boolean flag= false;

    public SensitiveOperationFlag(){
        if(!doSecurityCheck()){
            throw new SecurityException("Security check failed!");
        }
        flag=true;
    }

    //Security check return false
    private boolean doSecurityCheck(){
        return false;
    }

    public void storeMoney(){
        if(!flag){
            System.out.println("Object is not initiated yet!");
            return;
        }
        System.out.println("Store 1000000 RMB!");
    }
}

 注意,这里flag需要设置为volatile,只有这样才能保证构造函数在flag设置之前执行。也就是说需要保证happens-before特性。

使用this或者super

在JDK6或者更高版本中,如果对象的构造函数在java.lang.Object构造函数退出之前引发异常,则JVM将不会执行该对象的finalize方法。

因为Java确保java.lang.Object构造函数在任何构造函数的第一条语句之上或之前执行。如果构造函数中的第一个语句是对超类的构造函数或同一个类中的另一个构造函数的调用,则java.lang.Object构造函数将在该调用中的某个位置执行。否则,Java将在该构造函数的代码中的任何一个执行之前执行超类的默认构造函数,并且将通过隐式调用执行java.lang.Object构造函数。

也就是说如果异常发生在构造函数中的第一条this或者super中的时候,JVM将不会调用对象的finalize方法:

public class SensitiveOperationThis {

    public SensitiveOperationThis(){
        this(doSecurityCheck());
    }

    private SensitiveOperationThis(boolean secure) {
    }

    //Security check return false
    private static boolean doSecurityCheck(){
         throw new SecurityException("Security check failed!");
    }

    public void storeMoney(){
        System.out.println("Store 1000000 RMB!");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值