java 如何将 setAccessible 限制为仅“合法”使用?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2481862/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to limit setAccessible to only "legitimate" uses?
提问by polygenelubricants
The more I learned about the power of java.lang.reflect.AccessibleObject.setAccessible, the more astonished I am at what it can do. This is adapted from my answer to the question (Using reflection to change static final File.separatorChar for unit testing).
我对 的力量了解得越多,我java.lang.reflect.AccessibleObject.setAccessible就越惊讶于它的能力。这改编自我对问题的回答(使用反射更改静态最终 File.separatorChar 进行单元测试)。
import java.lang.reflect.*;
public class EverythingIsTrue {
static void setFinalStatic(Field field, Object newValue) throws Exception {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
public static void main(String args[]) throws Exception {
setFinalStatic(Boolean.class.getField("FALSE"), true);
System.out.format("Everything is %s", false); // "Everything is true"
}
}
You can do truly outrageous stuff:
你可以做真正令人发指的事情:
public class UltimateAnswerToEverything {
static Integer[] ultimateAnswer() {
Integer[] ret = new Integer[256];
java.util.Arrays.fill(ret, 42);
return ret;
}
public static void main(String args[]) throws Exception {
EverythingIsTrue.setFinalStatic(
Class.forName("java.lang.Integer$IntegerCache")
.getDeclaredField("cache"),
ultimateAnswer()
);
System.out.format("6 * 9 = %d", 6 * 9); // "6 * 9 = 42"
}
}
Presumably the API designers realize how abusable setAccessiblecan be, but must have conceded that it has legitimate uses to provide it. So my questions are:
大概 API 设计者意识到setAccessible可以被滥用的程度,但必须承认它具有合法用途来提供它。所以我的问题是:
- What are the truly legitimate uses for
setAccessible?- Could Java has been designed as to NOT have this need in the first place?
- What would the negative consequences (if any) of such design be?
- Can you restrict
setAccessibleto legitimate uses only?- Is it only through
SecurityManager?- How does it work? Whitelist/blacklist, granularity, etc?
- Is it common to have to configure it in your applications?
- Can I write my classes to be
setAccessible-proof regardless ofSecurityManagerconfiguration?- Or am I at the mercy of whoever manages the configuration?
- Is it only through
- 什么是真正合法的用途
setAccessible?- Java 是否可以设计为一开始就没有这种需求?
- 这种设计的负面后果(如果有的话)是什么?
- 您可以仅限
setAccessible于合法用途吗?- 是不是只能通过
SecurityManager?- 它是如何工作的?白名单/黑名单、粒度等?
- 必须在您的应用程序中配置它是否很常见?
setAccessible无论SecurityManager配置如何,我都可以编写我的类来证明吗?- 还是我任由管理配置的人摆布?
- 是不是只能通过
I guess one more important question is: DO I NEED TO WORRY ABOUT THIS???
我想还有一个更重要的问题是:我需要担心这个吗???
None of my classes have any semblance of enforceable privacy what-so-ever. The singleton pattern (putting doubts about its merits aside) is now impossible to enforce. As my snippets above show, even some basic assumptions of how Java fundamental works is not even close to being guaranteed.
我的任何课程都没有任何可强制执行的隐私。单例模式(将对其优点的怀疑放在一边)现在无法执行。正如我上面的片段所示,即使是关于 Java 基础如何工作的一些基本假设也无法保证。
ARE THESE PROBLEMS NOT REAL???
这些问题不是真的吗???
Okay, I just confirmed: thanks to setAccessible, Java strings are NOTimmutable.
好的,我刚刚确认:感谢setAccessible,Java 字符串不是一成不变的。
import java.lang.reflect.*;
public class MutableStrings {
static void mutate(String s) throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
value.set(s, s.toUpperCase().toCharArray());
}
public static void main(String args[]) throws Exception {
final String s = "Hello world!";
System.out.println(s); // "Hello world!"
mutate(s);
System.out.println(s); // "HELLO WORLD!"
}
}
Am I the only one who thinks this is a HUGE concern?
只有我认为这是一个巨大的问题吗?
回答by Sami Koivu
DO I NEED TO WORRY ABOUT THIS???
我需要担心这个吗???
That depends entirely on what types of programs you're writing and for what kind of an architecture.
这完全取决于您正在编写什么类型的程序以及用于哪种架构。
If you're distributing a software component called foo.jar to the people of the world, you're completely at their mercy anyway. They could modify the class definitions inside your .jar (through reverse engineering or direct bytecode manipulation). They could run your code in their own JVM, etc. In this case worrying will do you no good.
如果您将一个名为 foo.jar 的软件组件分发给全世界的人们,那么无论如何您都完全受他们的摆布。他们可以修改 .jar 中的类定义(通过逆向工程或直接字节码操作)。他们可以在他们自己的 JVM 等中运行您的代码。在这种情况下,担心对您没有好处。
If you're writing a web-application that only interfaces with people and systems via HTTP and you control the application server, it's also not a concern. Sure the fellow coders at your company may create code that breaks your singleton pattern, but only if they really want to.
如果您正在编写仅通过 HTTP 与人和系统交互的 Web 应用程序,并且您控制应用程序服务器,那么这也不是问题。当然,您公司的其他编码人员可能会创建打破单例模式的代码,但前提是他们真的想要这样做。
If your future job is writing code at Sun Microsystems/Oracle and you're tasked with writing code for the Java core or other trusted components, it's something you should be aware of. Worrying, however, will just make you lose your hair. In any case they'll probably make you read the Secure Coding Guidelinesalong with internal documentation.
如果您未来的工作是在 Sun Microsystems/Oracle 编写代码,并且您的任务是为 Java 核心或其他受信任的组件编写代码,那么您应该注意这一点。然而,担心只会让你脱发。无论如何,他们可能会让您阅读安全编码指南和内部文档。
If you're going to be writing Java applets, the security framework is something you should be aware of. You'll find that unsigned applets trying to call setAccessible will just result in a SecurityException.
如果您打算编写 Java 小程序,那么您应该了解安全框架。您会发现尝试调用 setAccessible 的未签名小程序只会导致 SecurityException。
setAccessible is not the only thing that goes around conventional integrity checks. There's a non-API, core Java class called sun.misc.Unsafe that can do pretty much anything at all it wants to, including accessing memory directly. Native code (JNI) can go around this kind of control as well.
setAccessible 不是唯一绕过传统完整性检查的东西。有一个名为 sun.misc.Unsafe 的非 API 核心 Java 类,它几乎可以做任何它想做的事情,包括直接访问内存。本机代码 (JNI) 也可以绕过这种控制。
In a sandboxed environment (for example Java Applets, JavaFX), each class has a set of permissions and access to Unsafe, setAccessible and defining native implementations are controlled by the SecurityManager.
在沙盒环境(例如 Java Applets、JavaFX)中,每个类都有一组权限和对 Unsafe、setAccessible 和定义本机实现的访问由 SecurityManager 控制。
"Java access modifiers are not intended to be a security mechanism."
“Java 访问修饰符并非旨在成为一种安全机制。”
That very much depends on where the Java code is being run. The core Java classes do use access modifiers as a security mechanism to enforce the sandbox.
这在很大程度上取决于运行 Java 代码的位置。核心 Java 类确实使用访问修饰符作为安全机制来强制执行沙箱。
What are the truly legitimate uses for setAccessible?
setAccessible 的真正合法用途是什么?
The Java core classes use it as an easy way to access stuff that has to remain private for security reasons. As an example, the Java Serialization framework uses it to invoke private object constructors when deserializing objects. Someone mentioned System.setErr, and it would be a good example, but curiously the System class methods setOut/setErr/setIn all use native code for setting the value of the final field.
Java 核心类使用它作为访问出于安全原因必须保持私有的内容的简单方法。例如,Java 序列化框架在反序列化对象时使用它来调用私有对象构造函数。有人提到了 System.setErr,这将是一个很好的例子,但奇怪的是 System 类方法 setOut/setErr/setIn 都使用本机代码来设置最终字段的值。
Another obvious legitimate use are the frameworks (persistence, web frameworks, injection) that need to peek into the insides of objects.
另一个明显的合法用途是需要窥探对象内部的框架(持久性、Web 框架、注入)。
Debuggers, in my opinion, don't fall into this category, as they normally don't run in the same JVM process, but instead the interface with the JVM using other means (JPDA).
在我看来,调试器不属于这一类,因为它们通常不在同一个 JVM 进程中运行,而是使用其他方式 (JPDA) 与 JVM 进行接口。
Could Java has been designed as to NOT have this need in the first place?
Java 是否可以设计为一开始就没有这种需求?
That's a pretty deep question to answer well. I imagine yes, but you'd need to add some other mechanism(s) that might not be all that preferrable.
这是一个很深的问题,很好回答。我想是的,但是您需要添加一些其他可能不是那么可取的机制。
Can you restrict setAccessible to legitimate uses only?
您可以将 setAccessible 限制为仅用于合法用途吗?
The most straight-forward OOTB restriction you can apply is to have a SecurityManager and allow setAccessible only to code coming from certain sources. This is what Java already does - the standard Java classes that come from your JAVA_HOME are allowed to do setAccessible, while unsigned applet classes from foo.com aren't allowed to do setAccessible. As was said before, this permission is binary, in the sense that one either has it or not. There is no obvious way to allow setAccessible to modify certain fields/methods while disallowing others. Using the SecurityManager you could, however, disallow classes from referencing certain packages completely, with or without reflection.
您可以应用的最直接的 OOTB 限制是拥有一个 SecurityManager 并仅允许 setAccessible 来自某些来源的代码。这是 Java 已经做的 - 来自 JAVA_HOME 的标准 Java 类可以执行 setAccessible,而来自 foo.com 的未签名小程序类不允许执行 setAccessible。如前所述,此权限是二元的,从某种意义上说,要么拥有,要么不拥有。没有明显的方法可以允许 setAccessible 修改某些字段/方法同时禁止其他字段/方法。但是,使用 SecurityManager 您可以禁止类完全引用某些包,无论是否有反射。
Can I write my classes to be setAccessible-proof regardless of SecurityManager configuration? ... Or am I at the mercy of whoever manages the configuration?
无论 SecurityManager 配置如何,我都可以将我的类编写为 setAccessible-proof 吗?... 或者我是否受到管理配置的人的支配?
You can't and you most certainly are.
你不能,你肯定是。
回答by Stephen C
- What are the truly legitimate uses for
setAccessible?
- 什么是真正合法的用途
setAccessible?
Unit testing, internals of the JVM (e.g. implementing System.setError(...)) and so on.
单元测试、JVM 内部(例如实现System.setError(...))等。
- Could Java has been designed as to NOT have this need in the first place?
- What would the negative consequences (if any) of such design be?
- Java 是否可以设计为一开始就没有这种需求?
- 这种设计的负面后果(如果有的话)是什么?
Lots of things would be unimplementable. For example, various Java persistence, serialization and dependency injections are reliant on reflection. And pretty much anything that relies on the JavaBeans conventions at runtime.
很多事情将无法实现。例如,各种 Java 持久化、序列化和依赖注入都依赖于反射。几乎所有在运行时依赖于 JavaBeans 约定的东西。
- Can you restrict
setAccessibleto legitimate uses only?- Is it only through
SecurityManager?
- 您可以仅限
setAccessible于合法用途吗?- 是不是只能通过
SecurityManager?
Yes.
是的。
- How does it work? Whitelist/blacklist, granularity, etc?
- 它是如何工作的?白名单/黑名单、粒度等?
It depends on the permission, but I believe that the permission to use setAccessibleis binary. If you want granularity, you need to either use a different class loader with a different security manager for the classes that you want to restrict. I guess you could implement a custom security manager that implements finer grained logic.
这取决于权限,但我认为使用权限setAccessible是二进制的。如果您想要粒度,您需要为要限制的类使用不同的类加载器和不同的安全管理器。我猜你可以实现一个自定义的安全管理器来实现更细粒度的逻辑。
- Is it common to have to configure it in your applications?
- 必须在您的应用程序中配置它是否很常见?
No.
不。
- Can I write my classes to be
setAccessible-proof regardless ofSecurityManagerconfiguration?
- Or am I at the mercy of whoever manages the configuration?
setAccessible无论SecurityManager配置如何,我都可以编写我的类来证明吗?
- 还是我任由管理配置的人摆布?
No you cannot, and yes you are.
不,你不能,是的,你是。
The other alternative is to "enforce" this via source-code analysis tools; e.g. custom pmdor findbugsrules. Or selective code review of code identified by (say) grep setAccessible ....
另一种选择是通过源代码分析工具“强制”执行此操作;例如习惯pmd或findbugs规则。或者对由 (say) 标识的代码进行选择性代码grep setAccessible ...。
In response to the followup
回应后续
None of my classes have any semblance of enforceable privacy what-so-ever. The singleton pattern (putting doubts about its merits aside) is now impossible to enforce.
我的任何课程都没有任何可强制执行的隐私。单例模式(将对其优点的怀疑放在一边)现在无法执行。
If that worries you, then I suppose you need to worry. But really you should not be trying to forceother programmers to respect your design decisions. If people are stupid enough to use reflection to gratuitously create multiple instances of your singletons (for example), they can live with the consequences.
如果这让你担心,那么我想你需要担心。但实际上,您不应该试图强迫其他程序员尊重您的设计决策。如果人们愚蠢到使用反射来无端地创建单身人士的多个实例(例如),他们可以忍受后果。
On the other hand, if you mean "privacy" to encompass the meaning of protecting sensitive information from disclosure, you are barking up the wrong tree. The way to protect sensitive data in a Java application is not to allow untrusted code into the security sandbox that deals with sensitive data. Java access modifiers are not intended to be a security mechanism.
另一方面,如果您的意思是“隐私”来包含保护敏感信息免遭泄露的含义,那么您就错了。在 Java 应用程序中保护敏感数据的方法是不允许不受信任的代码进入处理敏感数据的安全沙箱。Java 访问修饰符并非旨在成为一种安全机制。
<String example> - Am I the only one who thinks this is a HUGE concern?
<String example> - 只有我认为这是一个巨大的问题吗?
Probably not the onlyone :-). But IMO, this is not a concern. It is accepted fact that untrusted code should be executed in a sandbox. If you have trusted code / a trusted programmer doing things like this, then your problems are worse than unexpectedly mutable Strings. (Think logic bombs, exfiltration of data via covert channels, etcetera)
可能不是唯一一个:-)。但是 IMO,这不是问题。不受信任的代码应该在沙箱中执行是公认的事实。如果你有受信任的代码/受信任的程序员在做这样的事情,那么你的问题比意外可变的字符串更糟糕。(想想逻辑炸弹,通过隐蔽渠道泄露数据等)
There are ways to deal with (or mitigate) the problem of a "bad actor" in your development or operations team. But they are costly and restrictive ... and overkill for most use-cases.
有多种方法可以处理(或减轻)开发或运营团队中“不良行为者”的问题。但它们成本高昂且具有限制性……而且对于大多数用例来说太过分了。
回答by ewernli
Reflection is indeed orthogonal to safety/security under this perspective.
从这个角度来看,反射确实与安全/安保正交。
How can we limit reflection?
我们如何限制反射?
Java has security manager and ClassLoaderas foundations to its security model. In your case, I guess you need to look at java.lang.reflect.ReflectPermission.
Java 具有安全管理器并ClassLoader作为其安全模型的基础。就您而言,我想您需要查看java.lang.reflect.ReflectPermission.
But this does not completely solve the problem of reflection. The reflective capabilities that are available should be subject to a fine grained authorization scheme which is not the case now. E.g. to allow certain framework to use reflection (e.g. Hibernate), but no the rest of your code. Or to allow a program to reflect only in a read-only way, for debugging purpose.
但这并不能完全解决反射问题。可用的反射功能应该受制于细粒度的授权方案,而现在情况并非如此。例如,允许某些框架使用反射(例如 Hibernate),但不允许使用其余的代码。或者允许程序仅以只读方式反映,以用于调试目的。
One approach that may become mainstream in the future is the usage of so-called mirrorsto separate reflective capabilities from classes. See Mirrors: Design Principles for Meta-level Facilities. There are however various other researchthat tackles this issue. But I agree that the problem is more severe for dynamic language than static languages.
未来可能成为主流的一种方法是使用所谓的镜像将反射功能与类分开。参见镜子:元级设施的设计原则。然而,还有其他各种研究可以解决这个问题。但我同意动态语言的问题比静态语言更严重。
Should we be worried of the superpower that reflection gives us?Yes and no.
我们是否应该担心反思赋予我们的超能力?是和否。
Yesin the sense that the Java platform is supposed to be secured with Classloaderand security manager. The ability to mess with reflection can be see as a breach.
是的,因为 Java 平台应该使用Classloader安全管理器来保护。干扰反射的能力可以被视为一种破坏。
Noin the sense that most system are anyway not entirely secure. A lot of classes can frequently be subclassed and you could potentially already abuse the system with just that. Of course classes can be made final, or sealedso that they can not be subclassed in other jar. But only few classes are secured correctly (e.g. String) according to this.
不,因为大多数系统无论如何都不是完全安全的。许多类经常可以被子类化,并且您可能已经滥用系统。当然,类可以被制作final或密封,这样它们就不能在其他 jar 中被子类化。但是根据此,只有少数类被正确保护(例如字符串)。
See this answer about final classfor a nice explanation. See also the blog from Sami Koivufor more java hacking around security.
请参阅this answer about final class以获得很好的解释。另请参阅Sami Koivu的博客,了解更多有关安全性的 Java 黑客攻击。
The security model of Java can be seen as insufficient to some regard. Some languages such as NewSpeaktake even more radical approach to modularity, where you have access only to what is explicitly given to you by dependency inversion (by default nothing).
Java 的安全模型在某些方面被认为是不够的。一些语言(例如NewSpeak)采用更激进的模块化方法,您只能访问依赖倒置(默认情况下没有)明确提供给您的内容。
It's also important to note that security is anyway relative. At the language level, you can for instance not prevent a module form consuming 100% of CPU or consuming all memory up to a OutOfMemoryException. Such concerns need to be addressed by other means. We will maybe see in the future Java extended with resource utilization quotas, but it's not for tomorrow :)
同样重要的是要注意安全无论如何都是相对的。例如,在语言级别,您无法阻止模块形式消耗 100% 的 CPU 或消耗所有内存,最多OutOfMemoryException. 需要通过其他方式解决此类问题。我们可能会在未来看到 Java 扩展了资源利用率配额,但这不是明天:)
I could expand more on the subject, but I think I've made my point.
我可以在这个主题上进一步扩展,但我想我已经表达了我的观点。

