简体   繁体   English

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

[英]What are the implications of Java strings not really being immutable?

Background 背景

In Java 101, we're taught: 在Java 101中,我们被教导:

A String is immutable. String是不可变的。

Yes. 是。 Good. 好。 Thanks. 谢谢。

Then we get to Java 102 (or perhaps Java 201), and we discover: 然后我们到达Java 102(或者Java 201),我们发现:

A String isn't really immutable: you can change it using reflection. String不是真正不可变的:你可以使用反射来改变它。

Ah. 啊。 Fine. 精细。 Either quite cute or immeasurably perverse, depending on your perspective. 根据你的观点,要么非常可爱,要么无法估量。

These things, thus far, have been discussed ad infinitum on Stack Overflow and elsewhere. 到目前为止,这些事情已经在Stack Overflow和其他地方无限讨论 I am taking this much for granted in framing this question. 在构思这个问题时,我认为这是理所当然的。

What I'm interested to ask is this: 我有兴趣问的是:

The question 这个问题

Once we discover that a String isn't really immutable, what are the implications for how we should write our code? 一旦我们发现String不是真正不可变的,那么我们应该如何编写代码有什么意义呢?

Let me make that more specific in two respects. 让我在两个方面更具体。

JVM Sandboxing JVM沙盒

Is it possible for a malicious class to use this trick to break the security of the JVM, and get up to tricks that it shouldn't? 恶意类是否有可能使用此技巧来破坏JVM的安全性,并采取不应该使用的技巧? Are there places in the JDK, for instance, where a String is returned from a method, and it's safe only on the assumption that it can't be changed? 例如,JDK中是否存在从方法返回String位置,并且只有在无法更改的情况下才安全?

Here's a promising start: 这是一个很有希望的开始:

    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));

What this does is to retrieve a system property from the JVM, which comes back as a String , and then alter the String ; 这样做是从JVM中检索系统属性,它以String返回,然后改变String ; the consequence is that the underlying property is changed too. 结果是基础财产也发生了变化。 Can this be used to wreak havoc? 这可以用来造成严重破坏吗?

I'm not certain. 我不确定。 It's worth noting that you have to have the right permissions to perform reflection. 值得注意的是,您必须拥有执行反射的权限。 Is the security game already up by that point? 那个安全游戏已经上升了吗? It allows me here to get round not having permissions to change security properties, but is that to be expected, or is it a problem? 它允许我在这里没有更改安全属性的权限,但这是预期的,还是一个问题?

Are there ways of extending this to do something much worse? 有没有办法扩展这个做更糟糕的事情?

Defensive cloning 防守克隆

We're always told that it's a good idea to clone arrays and other objects before passing them to methods that might modify them, unless we want them to be modified. 我们总是被告知,在将数组和其他对象传递给可能修改它们的方法之前克隆它们是个好主意,除非我们希望它们被修改。 It's just good coding practice. 这只是很好的编码实践。 It's your own silly fault if you give someone the only copy of your array and it comes back messed with. 如果你给某人一个阵列的唯一副本并且它回来搞砸了,这是你自己的愚蠢错误。

But it looks as though the same argument ought to apply to a String ! 但看起来同样的论点应该适用于String I have never, ever heard anyone say that we ought to clone a String before passing it to a method someone else has written. 我从来没有听过任何人说我们应该克隆一个String然后再将其传递给别人写的方法。 But how is this any different, if a String is as mutable as an array? 但是,如果一个String像数组一样可变,那么这有什么不同呢?

Argument in favour of defensively cloning strings 支持防守克隆字符串的争论

If defensive cloning is all about not really knowing what a method might do to the things we pass it, and if what we're passing it is mutable, then we ought to clone it. 如果防御性克隆完全不知道方法可以对我们传递的东西做什么,如果我们传递的东西是可变的,那么我们应该克隆它。 If the code might be malicious, then it might alter a String ; 如果代码可能是恶意的,那么它可能会改变String ; so whenever it's important that the String stay unchanged, we should make sure we send a copy rather than the real thing. 因此,无论何时String保持不变都很重要,我们应该确保发送副本而不是真实的副本。

It won't wash to say that untrusted code ought to be run under a security manager if we don't trust it not to do bad things to the String . 如果我们不相信它不会对String做坏事,那么不应该说不受信任的代码应该在安全管理器下运行。 If that were true for a String , it would be true for an array; 如果对于String这是真的,那么数组就是如此; but no one ever says that cloning of arrays and other objects is only to be done in cases where you're also locking the code down with a security manager. 但是没有人说只有在你用安全管理器锁定代码的情况下才能克隆数组和其他对象。

Argument against defensively cloning strings 反对防守克隆字符串的争论

An array might get modified just by sloppy coding; 一个数组可能只是通过草率编码来修改; in other words, it might get modified unintentionally. 换句话说,它可能会被无意中修改。 But no one alters a String unintentionally: it can be done only with sneaky tricks that mean the programmer was trying to break immutability. 但是没有人无意中改变了一个String :它只能用偷偷摸摸的技巧来完成,这意味着程序员试图打破不变性。

That makes the two cases different. 这使得两种情况不同。 We really shouldn't be passing an array, even a clone of it, to code that we don't trust on any level. 我们真的不应该将数组(甚至是它的克隆)传递给我们在任何级别上都不信任的代码。 You don't download a library from somewhere dodgy and then think you're OK because you cloned your array. 你没有从某个地方下载一个库,然后认为你没事,因为你克隆了你的数组。 You clone defensively because you think the programmer might be making mistakes or making different assumptions from you about what's allowable with the data you're sending. 你在防御性方面克隆是因为你认为程序员可能会犯错误或对你发送的数据做出不同的假设。 If you're worried that the code might be modifying String s behind your back, you really shouldn't be running the code at all. 如果您担心代码可能会修改后面的String ,那么您根本不应该运行代码。 All you achieve by cloning a String is performance overhead. 通过克隆String实现的只是性能开销。

The answer? 答案?

How should we think about this? 我们该怎么想呢? Can JVM sandboxing be broken using these tricks? 使用这些技巧可以打破JVM沙盒吗? And should we code defensively in the light of the mutability of a String , or is this all a red herring? 我们应该根据String的可变性进行防御性编码,还是这些都是红鲱鱼?

There is a security setting that can be enabled for your Java program. 可以为Java程序启用安全设置。 According to the Javadocs for setAccessible , 根据setAccessibleJavadocs

First, if there is a security manager, its checkPermission method is called with a ReflectPermission("suppressAccessChecks") permission. 首先,如果有安全管理器,则使用ReflectPermission(“suppressAccessChecks”)权限调用其checkPermission方法。

A SecurityException is raised if flag is true but accessibility of this object may not be changed (for example, if this element object is a Constructor object for the class Class). 如果flag为true,则引发SecurityException,但不能更改此对象的可访问性(例如,如果此元素对象是类Class的Constructor对象)。

A SecurityException is raised if this object is a Constructor object for the class java.lang.Class, and flag is true. 如果此对象是类java.lang.Class的Constructor对象,并且flag为true,则引发SecurityException。

So, with a SecurityManager that doesn't allow this check, you can prevent any code from successfully calling setAccessible , preserving the immutability of String s. 因此,使用不允许此检查的SecurityManager ,可以防止任何代码成功调用setAccessible ,从而保留String的不变性。

Java has mechanisms in place to prevent this. Java有适当的机制来防止这种情况发生。 Specifically, for example, in order to subvert the String, you had to call field.setAccessible(true); 具体来说,例如,为了破坏String,你必须调用field.setAccessible(true); , which, for you, works, but if you look at the documentation, that setAccessible(true) call can throw SecurityException . 对于你来说,这是有效的,但是如果你查看文档,那个setAccessible(true)调用就会抛出SecurityException

Java allows you to install, or modify Security Managers that can lock down these reflection APIs. Java允许您安装或修改可以锁定这些反射API的安全管理器。 For example, consider Ideone which allows you to run limited Java applications. 例如,考虑Ideone,它允许您运行有限的Java应用程序。 In large part they do this by limiting the security permissions of the system. 在很大程度上,他们通过限制系统的安全权限来做到这一点。

You should read up on the SecuityManager and also the trail document 您应该阅读SecuityManager以及跟踪文档

You should also read through 你也应该通读

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM