写点什么

java 安全编码指南之:Mutability 可变性

发布于: 2020 年 09 月 03 日
java安全编码指南之:Mutability可变性

简介

mutable(可变)和 immutable(不可变)对象是我们在 java 程序编写的过程中经常会使用到的。


可变类型对象就是说,对象在创建之后,其内部的数据可能会被修改。所以它的安全性没有保证。


而不可变类型对象就是说,对象一旦创建之后,其内部的数据就不能够被修改,我们可以完全相信这个对象。


虽然 mutable 对象安全性不够,但是因为其可以被修改,所以会有效的减少对该对象的拷贝。


而 immutable 对象因为不可改变,所以尝试对该对象的修改都会导致对象的拷贝,从而生成新的对象。


我们最常使用的 String 就是一个 immutable 对象。


那么可变性在 java 的安全编码中的最佳实践是怎么样的呢? 一起来看看吧。


可变对象和不可变对象

知道了可变对象和不可变对象的不同之处之后,我们看一下怎么才能判断这个对象是可变对象还是不可变对象呢?


首先,最简单的一点就是,不可变对象创建之后就不能够被修改,所以不可变对象里面基本上没有 setXXX 之类的方法,而可变对象提供了 setXXX 这些可以修改内部变量状态的方法。


看一个例子 java.util.Date 是一个可变对象,而 java.time.LocalTime 是不可变对象。


看下他们的方法定义有什么区别呢?



首先是 Date,我们可以看到在其中定义了很多 setXXX 方法。



而在 LocalTime 中,我们基本上看不到 setXXX 方法。


同时不可变对象的字段基本上都是 final 的,防止被二次修改。


第二,不可变对象一般来说是不可继承的,在 java 中就是以 final 关键字做限定的:


public class Datepublic final class LocalTime
复制代码


第三,不可变对象一般会隐藏构造函数,而是使用类似工厂模式的方法来创建对象,这样为实例的创建提供了更多的机动性。


创建 mutable 对象的拷贝

那么如果我们想使用 mutable 对象,又不想被别人修改怎么办呢?


简单的办法就是拷贝一份要使用的对象:


public class CopyOutput {            private final java.util.Date date;            ...            public java.util.Date getDate() {                return (java.util.Date)date.clone();            }        }
复制代码


这里大家还要注意深拷贝和浅拷贝的问题。


为 mutable 类创建 copy 方法

既然要为 mutable 对象创建拷贝,那么相应的 mutable 类也需要提供一个 copy 方法来协助拷贝。


这里需要考虑一个深拷贝和浅拷贝的问题。


不要相信 equals

我们知道在 HashMap 中怎么去查找一个 key 呢?先去找这个 key 的 hash 值,然后去判断 key.equals 方法是否相等,考虑下面这种情况:


private final Map<Window,Extra> extras = new HashMap<>();
public void op(Window window) { Extra extra = extras.get(window); }
复制代码


op 方法接收一个 Window 对象,然后将其当成 key 从 HashMap 中取出对应的 value。


如果,这个时候,我们有一个类 A 继承了 Window,并且 hash 值和 equals 都和另外一个 Window 对象 B 相同,那么使用 A 这个 key 可以获取到 B 这个 key 存储的数据!


怎么解决这个问题呢?


Java 中有一个特别的 HashMap:IdentityHashMap,这个 Map 的 key 和 value 比较是用==而不是 equals 方法,所以可以有效的避免上面出现的问题。


private final Map<Window,Extra> extras = new IdentityHashMap<>();
public void op(Window window) { Extra extra = extras.get(window); }
复制代码


如果没有这样的 Map 可用,那么可以使用不可变对象作为 key 或者使用 Window 的私有变量,从而恶意攻击者无法获得这个变量。


public class Window {            /* pp */             class PrivateKey {                Window getWindow() {                    return Window.this;                }            }            final PrivateKey privateKey = new PrivateKey();
private final Map<Window.PrivateKey,Extra> extras = new WeakHashMap<>(); ... }
public class WindowOps { public void op(Window window) { // Window.equals may be overridden, // but safe as we don't use it. Extra extra = extras.get(window.privateKey); ... } }
复制代码


不要直接暴露可修改的属性

如果一个可变类中的某个属性确实需要暴露被外部使用,那么一定要将这个属性定义为 private,并且使用 wrapper 方法将其包装起来。


如果直接暴露出去,那么基本上就没有权限控制可言,任何程序只要能够拿到你这个对象,就可以对属性进行修改。考虑下下面的应用方式,我们在修改 state 的方法中加入了一个参数校验和权限控制。


public final class WrappedState {            // private immutable object            private String state;
// wrapper method public String getState() { return state; }
// wrapper method public void setState(final String newState) { this.state = requireValidation(newState); }
private static String requireValidation(final String state) { if (...) { throw new IllegalArgumentException("..."); } return state; } }
复制代码


public static fields 应该被置位 final

同样的,如果你是一个类变量,当然不希望这个变量会被任何人修改,那么需要将其置位 final。


public class Files {            public static final String separator = "/";            public static final String pathSeparator = ":";        }
复制代码


public static final field 应该是不可变的

如果类变量是 public static final 的,那么这个变量一定要是不可变的。


有人会问了,都定义成了 final 了,是不是就已经不可变了?


其实不然,比如我们定义了一个 final 的 List,虽然这个 list 不能变化,但是 list 里面的值是可以变化的。我们需要将可变变量修改为不可变变量,如下所示:


import static java.util.Arrays.asList;        import static java.util.Collections.unmodifiableList;        ...        public static final List<String> names = unmodifiableList(asList(            "Fred", "Jim", "Sheila"        ));
复制代码


如果使用 JDK9 中引入的 of()或者 ofEntries()方法,可以直接创建不可修改的集合:


public static final List<String> names = List.of("Fred", "Jim", "Sheila");
复制代码


本文已收录于 http://www.flydean.com/java-security-code-line-mutability/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!


发布于: 2020 年 09 月 03 日阅读数: 605
用户头像

关注公众号:程序那些事,更多精彩等着你! 2020.06.07 加入

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧,尽在公众号:程序那些事!

评论

发布
暂无评论
java安全编码指南之:Mutability可变性