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业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
零基础打造一款属于自己的网页搜索引擎
【前言】 在说这个之前,想必大家应该都比较了解搜索引擎了,它就是通过用户在浏览器输入框中输入文本,从而显示一些结果,你觉得哪项符合你要搜索的内容,你就点击哪项。 【一、项目准备】 浏览器:360浏览器 编辑器:Sublime Text 3 插件:Jquery-3.2.1.Min.Js 【二、项目实现】 由于是要实现一个网页搜索引擎,所以我们需要借用网页三剑客(Html+Css+Javascript),然后实现这一功能。 1.打开百度分析网页结构 我们可以先看看百度的搜索引擎: 可以看到,这个搜索框的部分设置,比如关闭自动完成功能。然后我们在随便搜索内容来查看它的变化: 可以看到某些我们查询的关键字,于是我们便发现了请求规律: https://www.baidu.com/s?+查询字符参数 这就构成了我们的一个完整的get请求,而且这里面有很多关键字参数可以省略掉,只需要保留重要的一部分就好了。于是,经试验,得出如下结论: https://www.baidu.com/s?wd=keyword 这个才是请求的接口地址,只需将keyword参数替换为任意搜索关键字即可实现查询并跳转到相应结果...
- 下一篇
应用系统瓶颈排查和分析的思考-Arthas 实战
作者 | 一啦米 【Arthas 官方社区正在举行征文活动,参加即有奖品拿~点击投稿】 背景 业务应用系统接入流程引擎来处理业务应用的流程执行,流程引擎提供多线程高性能异步化来执行流程元素的执行,但是如何设置流程引擎的线程池线程数执行,以及执行线程数和任务数,应用机器资源使用情况之间的关系如何,目前只能通过接入方的人工经验评估,比较粗泛评估,参数的合理性也很难直观的评估,现通过现有的应用系统默认的参数配置进行问题说明。 性能监控 查看 CPU 核数 应用机器的配置是 2 核 CPU 配置: 查看应用机器的负载情况 应用程序的近 15 分钟内的负载严重的超载(平均超出 CPU 执行的 10 倍之多),应用程序对应的 CPU 和 MEM 的利用率都很高,按照本次单机模拟线上的流量(QPS:10) 不至于出现如此的负载情况吧。 查看应用机器的综合统计 CPU 运行队列中也严重的堵筛(Procs列中指标),应用的 CPU 有效利用率其实并不算太高(US+SY),但是系统出现了严重的 CS(上下文切换),配合 BI,BO 执行,也间接反馈出一些端倪,具体反馈到应用程序中,还需要进一步分析。 查看...
相关文章
文章评论
共有0条评论来说两句吧...