为什么说String不可变是基于安全考虑?

相信很多人,在面试中经常被问到这样一个问题:String类型为什么被设计成不可变的?

通过百度、Google得知主要有两点原因:1)String Pool的需要,2)出于安全考虑。

第一个原因比较好理解,String是Java中是使用最多的数据类型,如果不能有效的管理string类型的变量,那对内存来说将是噩梦。于是JVM的设计者们就提出了String池的概念,String池仅仅是一个由许多唯一字符串组成的集合。String池一个很基本的思想就是,字符串一旦被创建就可以重复使用。通过这种方式,相同内容的字符串在代码中被创建20次,在String池中最终只有一个该字符串的实例。为了实现String池中字符串的共享,必须保证字符串不能被随意修改。
所以String的不变性是实现String池的前提
但是我们该怎么理解出于安全考虑?为什么被设计成不可变的就安全了;如果被设计成可变的,哪里就不安全了?接下来我们来看一个问题:

TOCTOU攻击

在解释这个问题之前,先来了解一下什么是TOCTOU(发音tock too)攻击。举个栗子(好吃的栗子),假设一个Web应用允许用户去编辑页面,但只能编辑被授权的页面。现在一个用户向服务器发出修改页面a.html的请求,服务器认证通过后,返回一个编辑页面让用户修改a.html。从认证通过到返回编辑页面之间是需要一点时间的,尽管可能是几毫秒甚至更短的时间。然而攻击者就可以利用这段时间向服务器发出攻击。可以在认证通过后,立刻修改发送给服务器的文件名称,把a.html修改成此用户无权访问的文件b.html。这样服务器就毫不知情把b.html返回给用户了。
看下面的伪代码:

//打开服务器中的文件file if(!access(file)){ //1     //授权失败,没有权限访问该文件     throw new SecurityException("拒绝访问"); }  //在这里file的内容可能被修改  //2 open(file);   //授权成功,打开此文件,进行读写操作。 //3 

如果文件名称在//2处被篡改,那么//3处打开的文件就不再是//1处认证通过的文件。这样就是TOCTOU攻击。

String的不变性有效防范TOCTOU攻击

怎么有效的防范这种攻击,Java的做法就是让传递的file参数不可被改变。
把上面的例子补充完整:

客户端

String url="a.html"; openFile(url);//1 

服务端

public void openFile(String file){     if(!access(file)){         throw new SecurityException("拒绝访问");     }      //2     open(file);//3 } 

现在假设String内容是可以被改变的。
url的值是a.html,授权成功后,代码执行到了//2处。在还没执行open方法前,通过a.replace("a", "b");把变量url的值篡改成b.html(可以通过新线程进行篡改) 。这样open方法打开的文件就变成了b.html(原本无权访问的文件)。
记住不要犯这样一个低级错误:认为在执行open方法前,通过url="b.html"就可以篡改url的值了。传递给openFile方法的是变量url的值的引用。改变url的指向对openFile方法是没有任何影响的。我们要做的是修改url的指向对应的内容。(为什么提到这一点呢,因为我犯过这个低级错误,哈哈)。
但实际上呢,String对象是不可以被改变的。不可被改变的意思是执行完a.replace("a", "b");后,url的值还是a.html,而replace方法创建了一个新的String对象b.html并返回。因此就防范了TOCTOU攻击。
现在你是不是更深刻的理解了下面这段代码为什么会输出a.html:

String a="a.html"; a.replace("a", "b"); System.out.println(a); 

这两天一直Google,希望找到权威一点的对String不可变的解释,最终找到了Java之父 James Gosling的一段采访记录。摘抄一段原话:

One of the things that forced Strings to be immutable was security. You have a file open method. You pass a String to it. And then it’s doing all kind of authentication checks before it gets around to doing the OS call.
If you manage to do something that effectively mutated the String, after the security check and before the OS call, then boom, you’re in. But Strings are immutable, so that kind of attack doesn’t work. That precise example is what really demanded that Strings be immutable.

原文链接:James Gosling on Java, May 2001
参考链接:
TOCTOU:Time Of Check to Time Of Use
Why String is Immutable in Java ?


发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注