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 Date public 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/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
肝完这篇前端开发学习路线图,凌晨两点了
交作业了 这篇让小伙伴们久等了。 本篇来梳理一下前端开发方向的学习路线和知识点,包括前端开发到底要学什么、有哪些重要知识、现在的主流的技术点有哪些,希望能给之前很多问过该类问题的小伙伴们一个交代。 在正式开始之前,先聊两个常见的问题吧。 前端和后端到底该怎么选? 不得不说,前端开发现在确实很火,学的人也炒鸡多,如果仅从岗位需求、工资水平等市场就业这个角度来看,其实基本上已经与后端开发不分伯仲了,而且这几年前端技术的发展也非常繁荣。所以到底选哪个方向就看大家自己的兴趣和自身的境况了,谁也没有办法说哪个就一定更好。 我这边能做的就是把前、后端各自的学习路线、知识点都梳理一遍,供小伙伴们参考,小伙伴们可以对照具体的技术点梳理,结合自身的实际情况,觉得对哪个更有兴趣、或者把握更大一点,再来做选择也不迟。 目前我们这里对于主流的几个方向岗位的知识点和路线都已经做过梳理了,小伙伴们可以按需查看: Java后端学习路线和知识点大梳理 C/C++开发(偏后台)学习路线和知识点大梳理 嵌入式开发学习路线和知识点大梳理 前端发展这么快,如何安身立命? 回看近年的前端发展,不管是之前的散装前端时代,还是后来...
- 下一篇
面向产品经理的简单物联网框架
在本文中,我将介绍一个物联网框架,以帮助产品经理解决物联网产品的复杂性。该框架提供了易于遵循的结构,可揭示物联网堆栈每一层的需求,包括业务决策,技术决策等。 物联网产品的管理可能会非常艰巨和令人困惑,即使对于经验丰富的产品经理而言也是如此。这是因为物联网产品比普通产品复杂,因为您需要考虑五层技术的复杂性:设备硬件,设备软件,通信,云平台和云应用程序。 您不仅需要在这五个层中的每一个上做出关键的业务和技术决策,而且还需要确保这无数决策与您的整体策略相一致并且在这五个层中都一致。这成倍地增加了管理物联网产品的难度。为了帮助产品经理解决这种复杂性,我了解到了一个称为IoT决策框架。 该物联网框架提供了一种结构化的方法来创建可靠的物联网产品策略。这种策略就是制定决策。物联网决策框架可帮助您了解决策领域,并确保所有战略决策的一致性。 物联网框架将帮助您及早将错误的时间和金钱投入到产品上,从而发现陷阱。 要开始使用IoT决策框架,让我们看一下IoT技术堆栈的五层。 物联网技术栈的5层 管理物联网解决方案的最大挑战是物联网技术栈中有五层,并且需要在每一层做出决策。 从角度看,SaaS应用程序仅在右侧...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2全家桶,快速入门学习开发网站教程
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装