java安全编码指南之:Mutability可变性详解
作者:flydean程序那些事 发布时间:2023-11-11 06:30:24
简介
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");
来源:https://blog.csdn.net/superfjj/article/details/108375525
猜你喜欢
- 本文实例讲述了C#实现Base64处理的加密解密,编码解码。分享给大家供大家参考,具体如下:using System;using Syste
- java.lang.ArrayStoreException 分析这个demo来说明怎样排查一个spring boot 1应用升级到sprin
- Navigator 的 push 和 pop方法Navigator 导航器的 push 和 pop 方法可以携带参数在页面间传递,其他变形的
- 光流的概念是由一个叫Gibson的哥们在1950年提出来的。它描述是空间运动物体在观察成像平面上的像素运动的瞬时速度,利用图像序列中像素在时
- 简介Android给我们提供了一种轻量级的异步任务类AsyncTask。该类中实现异步操作,并提供接口反馈当前异步执行结果及进度,这些接口中
- Java与C++实现相同的MD5加密算法1、Java版package com.lyz.utils.common;import java.io
- 前言本文主要给大家介绍的是关于Java对xls文件进行读写操作的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:wi
- 一、介绍在实际的软件项目开发过程中,我可以很负责任的跟大家说,如果你真的实际写代码的时间超过5年,你对增删改查这类简单的功能需求开发,可以说
- Android 集成FlutterFlutter 作为 Google 开源的新一代跨平台、高性能 UI 框架,旨在帮助开发者高效地构建出跨平
- 一、选择结构大纲if单选择结构if双选择结构if多选择结构嵌套的if结构switch多选择结构二、if单选择结构我们很多时候需要去判断一个东
- 1 自定义类加载器自定义类加载器的代码很简单,只需要继承ClassLoader类,覆写findClass方法即可其默认实现是会抛出一个异常:
- “Java is still not dead—and people are starting to figure that out.”本教
- 简介由于最近的项目需求,需要在把配置类导入到容器中,通过查询,使用@Import注解就能实现这个功能,@Import注解能够帮我们吧普通配置
- 本文介绍Android中的5种数据存储方式。数据存储在开发中是使用最频繁的,在这里主要介绍Android平台中实现数据存储的5种方式,分别是
- 1.前言在Mybatis中需要创建的配置文件有sqlMapconfig.xml,映射文件xxxMapper.xml,而这些文件在idea中并
- NDK部分1、下载ndk这里就一笔带过了。2、解压ndk不要解压,文件权限会出错。执行之,会自动解压,然后mv到想放的地方。我放到了”/us
- 前言最近项目需要和Oracle数据库进行交互,然后我从Maven中央仓库下载数据库驱动jar包,但怎么都下不下来,我到Oracle官网上一看
- 在style中如下面那样定义:<style name="mystyle"> <item name=&
- 一、注解(annotations)列表@SpringBootApplication:包含了@ComponentScan、@Configura
- 昨天在与对端系统调接口的时候,对端系统对我们传过去的json串老是处理不了,后来查原因是应为我们传过去的json串里有json对象数组,因为