如何将setAccessible限制为仅仅"合法"使用?

pol*_*nts 97 java security reflection

我越了解它的力量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"
   }
}
Run Code Online (Sandbox Code Playgroud)

你可以做真正令人发指的事情:

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"
   }
}
Run Code Online (Sandbox Code Playgroud)

据推测,API设计者意识到setAccessible可以滥用,但必须承认它有合法的用途来提供它.所以我的问题是:

  • 什么是真正合法的用途setAccessible
    • Java可以设计为首先没有这个需求吗?
    • 这种设计的负面后果(如果有的话)会是什么?
  • 你能仅限setAccessible于合法使用吗?
    • 它只是通过SecurityManager
      • 它是如何工作的?白名单/黑名单,粒度等?
      • 在您的应用程序中配置它是否常见?
    • setAccessible无论SecurityManager配置如何,我都可以编写我的课程吗?
      • 或者我受管理配置的人的摆布?

我想一个更重要的问题是:我是否需要担心这个?

我的课程都没有任何类似的可强制执行的隐私.单身模式(对其优点置之不理)现在无法实施.正如我上面的代码片段所示,甚至对Java基础工作原理的一些基本假设甚至都不尽如人意.

这些问题不是真的吗?


好的,我刚刚确认:谢谢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!"
   }
}
Run Code Online (Sandbox Code Playgroud)

我是唯一一个认为这是一个巨大关注的人吗?

Sam*_*ivu 103

我是否需要担心这个???

这完全取决于您正在编写的程序类型以及何种体系结构.

如果你正在向全世界的人们分发一个名为foo.jar的软件组件,那么无论如何你都完全处于怜悯之中.他们可以修改.jar中的类定义(通过逆向工程或直接字节码操作).他们可以在自己的JVM等中运行你的代码.在这种情况下,担心对你​​没有好处.

如果您正在编写一个仅通过HTTP与人员和系统连接并且您控制应用程序服务器的Web应用程序,那么它也不是一个问题.当然,贵公司的编码人员可能会创建破坏单身模式的代码,但前提是他们真的想要这样做.

如果您未来的工作是在Sun Microsystems/Oracle上编写代码,并且您的任务是为Java核心或其他可信组件编写代码,那么您应该注意这一点.然而,担心会让你失去头发.无论如何,他们可能会让您阅读安全编码指南以及内部文档.

如果您要编写Java applet,那么安全框架应该是您应该注意的.您会发现尝试调用setAccessible的未签名小程序只会导致SecurityException.

setAccessible并不是传统完整性检查的唯一方法.有一个名为sun.misc.Unsafe的非API核心Java类,它可以完成任何想做的事情,包括直接访问内存.本机代码(JNI)也可以绕过这种控制.

在沙盒环境中(例如Java Applets,JavaFX),每个类都具有一组权限,并且对Unsafe,setAccessible和定义本机实现的访问权限由SecurityManager控制.

"Java访问修饰符并不是一种安全机制."

这在很大程度上取决于Java代码的运行位置.核心Java类确实使用访问修饰符作为强制执行沙箱的安全机制.

setAccessible的真正合法用途是什么?

Java核心类使用它作为一种简单的方法来访问由于安全原因必须保持私有的东西.例如,Java Serialization框架在反序列化对象时使用它来调用私有对象构造函数.有人提到System.setErr,这将是一个很好的例子,但奇怪的是System类方法setOut/setErr/setIn都使用本机代码来设置final字段的值.

另一个显而易见的合法用途是需要查看对象内部的框架(持久性,Web框架,注入).

在我看来,调试器不属于这一类,因为它们通常不在同一个JVM进程中运行,而是使用其他方式(JPDA)与JVM的接口.

Java可以设计为首先没有这个需求吗?

这是一个非常深刻的问题,可以很好地回答.我想是的,但是你需要添加一些其他可能不是那么好的机制.

您是否可以将setAccessible限制为仅限合法用途?

您可以应用的最直接的OOTB限制是具有SecurityManager并且仅允许setAccessible来自某些来源的代码.这就是Java已经做的 - 允许来自JAVA_HOME的标准Java类执行setAccessible,而来自foo.com的未签名applet类不允许执行setAccessible.如前所述,这个权限是二元的,从某种意义上来说就是拥有它.没有明显的方法允许setAccessible修改某些字段/方法,同时禁止其他字段/方法.但是,使用SecurityManager,您可以禁止类完全引用某些包,无论是否有反射.

无论SecurityManager配置如何,我都可以将我的类编写为setAccessible-proof吗?......或者我受管理配置的人的摆布?

你不能和你肯定是.


Ste*_*n C 15

  • 什么是真正合法的用途setAccessible

单元测试,JVM的内部(例如实现System.setError(...))等.

  • Java可以设计为首先没有这个需求吗?
  • 这种设计的负面后果(如果有的话)会是什么?

很多事情都是无法实现的.例如,各种Java持久性,序列化和依赖性注入都依赖于反射.几乎所有在运行时都依赖于JavaBeans约定的东西.

  • 你能仅限setAccessible于合法使用吗?
  • 它只是通过SecurityManager

是.

  • 它是如何工作的?白名单/黑名单,粒度等?

这取决于许可,但我相信使用的许可setAccessible是二进制.如果需要粒度,则需要为要限制的类使用不同的类加载器和不同的安全管理器.我想你可以实现一个实现更细粒度逻辑的自定义安全管理器.

  • 在您的应用程序中配置它是否常见?

没有.

  • setAccessible无论SecurityManager配置如何,我都可以编写我的课程吗?
    • 或者我受管理配置的人的摆布?

不,你不能,是的,你是.

另一种方法是通过源代码分析工具"强制执行"; 例如习俗pmdfindbugs规则.或者(由)确定的代码的选择性代码审查grep setAccessible ....

回应后续行动

我的课程都没有任何类似的可强制执行的隐私.单身模式(对其优点置之不理)现在无法实施.

如果这让你担心,那么我想你需要担心.但实际上你不应该试图强迫其他程序员尊重你的设计决策.如果他们愚蠢到无偿地创建你的单身人士的多个实例(例如),他们就可以忍受后果.

如果你的意思是"隐私"来涵盖保护敏感信息免于泄露的含义,你也会咆哮错误的树.保护敏感数据的方法不是允许不受信任的代码进入处理敏感数据的安全沙箱.Java访问修饰符不是一种安全机制.

<String example> - 我是唯一一个认为这是一个巨大问题的人吗?

可能不是唯一的一个:-).但IMO,这不是一个问题.事实上,不受信任的代码应该在沙箱中执行.如果你有可靠的代码/一个受信任的程序员做这样的事情,那么你的问题就更糟糕了,因为有了意想不到的可变字符串.(想想逻辑炸弹......)


ewe*_*nli 11

在这个观点下,反思确实与安全/保障正交.

我们怎样才能限制反思?

Java具有安全管理器和ClassLoader安全模型的基础.在你的情况下,我想你需要看一下java.lang.reflect.ReflectPermission.

但这并不能完全解决反思问题.可用的反射功能应遵循细粒度的授权方案,而现在并非如此.例如,允许某些框架使用反射(例如Hibernate),但不使用其余的代码.或者允许程序仅以只读方式反映,以进行调试.

可能成为未来主流的一种方法是使用所谓的镜子将反射能力与类别分开.参见镜子:元级设施的设计原则.然而,还有其他各种研究可以解决这个问题.但我同意动态语言的问题比静态语言更严重.

我们应该担心反射给我们的超级大国吗?是的,不是.

的,Java平台应该由Classloader安全管理器保护.混乱反射的能力可以被视为违规行为.

没有在这个意义上,大多数系统都是反正不是完全安全的.许多类经常被子类化,你可能已经滥用了系统.当然可以制作final密封类,以便它们不能在其他jar中进行子类化.但根据这一点,只有少数类被正确保护(例如String).

有关最终类的详细解释,请参阅此答案.另请参阅Sami Koivu的博客,了解有关安全性的更多Java黑客攻击.

Java的安全模型在某些方面可能被认为是不够的.某些语言(如NewSpeak)采用了更为激进的模块化方法,在这种方法中,您只能访问依赖性反转明确给出的内容(默认情况下没有任何内容).

同样重要的是要注意安全性无论如何都是相对的.在语言级别,您可以例如不阻止模块表单消耗100%的CPU或消耗所有内存直到a OutOfMemoryException.需要通过其他方式解决这些问题.我们可能会在未来看到Java扩展了资源利用配额,但它不是明天:)

我可以在这个问题上进一步扩展,但我认为我已经说明了我的观点.