Java字符串不是真正不可变的含义是什么? 背景

Java字符串不是真正不可变的含义是什么? 背景,java,string,security,clone,immutability,Java,String,Security,Clone,Immutability,在Java 101中,我们学习到: 字符串是不可变的 对。好。谢谢 然后我们进入Java 102(或者Java 201),我们发现: 字符串并不是真正不变的:您可以使用反射来更改它 啊。好的要么非常可爱,要么极度反常,这取决于你的观点 到目前为止,这些问题一直存在于堆栈溢出和其他方面。在提出这个问题时,我认为这是理所当然的 我想问的是: 问题 一旦我们发现字符串并非真的是不可变的,那么我们应该如何编写代码的含义是什么? 让我从两个方面更具体地说明这一点 JVM沙箱 恶意类是否可能使用此技巧来破坏

在Java 101中,我们学习到:

字符串
是不可变的

对。好。谢谢

然后我们进入Java 102(或者Java 201),我们发现:

字符串
并不是真正不变的:您可以使用反射来更改它

啊。好的要么非常可爱,要么极度反常,这取决于你的观点

到目前为止,这些问题一直存在于堆栈溢出和其他方面。在提出这个问题时,我认为这是理所当然的

我想问的是:

问题 一旦我们发现
字符串
并非真的是不可变的,那么我们应该如何编写代码的含义是什么?

让我从两个方面更具体地说明这一点

JVM沙箱 恶意类是否可能使用此技巧来破坏JVM的安全性,并使用不应该使用的技巧?例如,JDK中是否有这样的地方:从方法返回
字符串
,并且只有在假设它不能更改的情况下才是安全的

这是一个充满希望的开端:

    String prop = "java.version";
    // retrieve a System property as a String
    String s = System.getProperty(prop);
    System.out.println(s);
    // now mess with it
    Field field = String.class.getDeclaredField("value");  
    field.setAccessible(true);  
    char[] value = (char[])field.get(s);  
    value[0] = 'x';
    //turns out we've changed not just the String we were
    //given but the underlying property too!
    System.out.println(System.getProperty(prop));
这样做的目的是从JVM中检索一个系统属性,该属性返回为
字符串
,然后更改
字符串
;其结果是基础属性也发生了变化。这能用来制造混乱吗

我不确定。值得注意的是,您必须拥有执行反射的正确权限。到那时安全游戏已经开始了吗?它允许我在这里绕过没有权限更改安全属性的问题,但这是意料之中的事,还是一个问题

有没有办法把它扩展到更糟糕的事情上

防御性克隆 我们总是被告知,在将数组和其他对象传递给可能修改它们的方法之前克隆它们是个好主意,除非我们希望修改它们。这只是一个很好的编码实践。如果你给了别人你的阵列的唯一副本,而它回来时却乱七八糟,那是你自己愚蠢的错误

但看起来似乎相同的参数应该应用于
字符串
!我从来没有听过有人说,在将
字符串传递给其他人编写的方法之前,我们应该克隆它。但是,如果
字符串
像数组一样可变,这又有什么不同呢

支持防御性克隆字符串的论点 如果防御性克隆就是不知道一种方法对我们传递的东西会有什么影响,如果我们传递的东西是可变的,那么我们应该克隆它。如果代码可能是恶意的,那么它可能会更改
字符串
;因此,每当
字符串保持不变很重要时,我们应该确保发送一份副本,而不是真实的内容

如果我们不相信不受信任的代码不会对
字符串做坏事,那么就不能说不受信任的代码应该在安全管理器下运行。如果这对于
字符串是真的,那么对于数组也是真的;但是,没有人说过,只有在使用安全管理器锁定代码的情况下,才能克隆阵列和其他对象

反对防御性克隆字符串的论点 一个数组可能仅仅因为草率的编码而被修改;换句话说,它可能会被无意中修改。但是没有人会无意中修改
字符串:这只能通过狡猾的技巧来完成,这意味着程序员试图打破不变性


这使得这两种情况有所不同。我们真的不应该将数组,甚至是它的克隆,传递给我们在任何级别都不信任的代码。你不会从某个不可靠的地方下载一个库,然后认为你没事,因为你克隆了你的阵列。你可以防御性地克隆,因为你认为程序员可能会犯错误,或者对你发送的数据的允许范围做出不同的假设。如果您担心代码可能在背后修改
String
s,那么您真的不应该运行代码。通过克隆
字符串
所实现的只是性能开销

答案是什么?
我们应该如何看待这一点?使用这些技巧可以打破JVM沙箱吗?我们是否应该根据
字符串的易变性进行防御性编码,或者这完全是一种误导?

可以为Java程序启用安全设置。据报道,

首先,如果有一个安全管理器,则使用ReflectPermission(“suppressAccessChecks”)权限调用其checkPermission方法

如果标志为true,但此对象的可访问性可能不会更改(例如,如果此元素对象是类的构造函数对象),则会引发SecurityException

如果此对象是类java.lang.class的构造函数对象,且标志为true,则会引发SecurityException


因此,使用不允许此检查的
SecurityManager
,您可以阻止任何代码成功调用
setAccessible
,从而保持
字符串的不变性。

Java有适当的机制来防止这种情况发生。具体来说,例如,为了破坏字符串,必须调用
field.setAccessible(true),这对您来说是有效的,但如果您查看文档,则
setAccessible(true)
call可以

Java允许您安装或修改可以锁定这些反射API的安全管理器。例如,考虑IDENON,它允许您运行有限的java应用程序。这在很大程度上是通过限制系统的安全权限来实现的

你应该仔细阅读,还有

你也应该通读一遍

  • 如何操作以及默认值是什么,等等

答案是:不允许在应用程序中使用反射,至少不允许在系统对象上使用反射。(任何人