为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

慕少森 2019-11-06 10:54:00

我对的力量了解得越多,我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"

   }

}

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


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"

   }

}

大概是API设计者意识到了可滥用性setAccessible,但是必须承认它具有合法的用途来提供它。所以我的问题是:


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

Java是否可以被设计为一开始就没有这种需求?

这种设计的负面后果(如果有)是什么?

您setAccessible只能限制合法使用吗?

只有通过SecurityManager吗?

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

必须在您的应用程序中对其进行配置是否常见?

setAccessible无论SecurityManager配置如何,我都可以将自己的类写成-proof 吗?

还是由谁来管理配置?

我猜一个更重要的问题是:我需要为此担心吗???


我的课程都没有任何类似的可执行隐私。现在无法执行单例模式(不考虑其优缺点)。正如我上面的摘录所示,甚至几乎无法保证有关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());

   }   

 

我是唯一认为这是一个巨大问题的人吗?


查看完整描述

3 回答

?
明月笑刀无情

我需要为此担心吗???


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


如果您要向世界各地的人们分发名为foo.jar的软件组件,则无论如何您完全会屈从于他们。他们可以修改.jar中的类定义(通过逆向工程或直接字节码操作)。他们可以在自己的JVM中运行您的代码,等等。在这种情况下,担心将无济于事。


如果您正在编写仅通过HTTP与人和系统交互的Web应用程序,并且可以控制应用程序服务器,那么也不必担心。确保您公司的其他编码人员可以创建破坏您的单例模式的代码,但前提是他们确实愿意。


如果您将来的工作是在Sun Microsystems / Oracle上编写代码,而您的任务是为Java核心或其他受信任的组件编写代码,那么您应该意识到这一点。但是,担心只会使您掉头发。无论如何,它们可能会让您阅读《安全编码指南》以及内部文档。


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


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


在沙盒环境(例如Java Applets,JavaFX)中,每个类都有一组权限,对不安全,setAccessible的访问以及定义的本机实现均由SecurityManager控制。


“ Java访问修饰符并非旨在成为一种安全机制。”


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


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


Java核心类使用它作为一种简单的方法来访问出于安全原因而必须保持私有的内容。例如,Java序列化框架在反序列化对象时使用它来调用私有对象构造函数。有人提到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的类吗?...还是由谁来管理配置?


您不能,而且您当然可以。


查看完整回答
反对 回复 2019-11-06
?
SMILET

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

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


Java是否可以被设计为一开始就没有这种需求?

这种设计的负面后果(如果有)是什么?

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


您setAccessible只能限制合法使用吗?

只有通过SecurityManager吗?

是。


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

这取决于权限,但我相信使用权限setAccessible是二进制的。如果需要粒度,则需要对要限制的类使用其他类加载器和其他安全管理器。我猜您可以实现一个实现更细粒度逻辑的自定义安全管理器。


必须在您的应用程序中对其进行配置是否常见?

没有。


setAccessible无论SecurityManager配置如何,我都可以将自己的类写成-proof 吗?

还是由谁来管理配置?

不,你不能,是的。


另一种选择是通过源代码分析工具“强制”执行此操作。例如风俗pmd或findbugs规则。或(例如)识别的代码的选择性代码审查grep setAccessible ...。


作为对后续行动的回应


我的课程都没有任何类似的可执行隐私。现在无法执行单例模式(不考虑其优缺点)。


如果那让您担心,那么我想您需要担心。但是,实际上,您不应该试图迫使其他程序员尊重您的设计决策。如果人们愚蠢到可以使用反射无意中创建多个单例实例(例如),那么他们可以承受后果。


另一方面,如果您的意思是“隐私”包含保护敏感信息不被泄露的含义,那么您就是在树错误的树。在Java应用程序中保护敏感数据的方法是不允许不信任的代码进入处理敏感数据的安全沙箱中。Java访问修饰符并非旨在成为一种安全机制。


<字符串示例>-我是唯一认为这是一个巨大问题的人吗?


可能不是唯一的一个:-)。但是海事组织,这不是一个问题。公认的事实是,不应在沙箱中执行不受信任的代码。如果您拥有受信任的代码/受信任的程序员从事此类工作,那么您的问题就会比意外地可变的字符串更糟糕。(想想逻辑炸弹,通过秘密渠道泄露数据等)


在您的开发或运营团队中,有许多方法可以解决(或减轻)“不良行为者”的问题。但是它们是昂贵且限制性的...对于大多数用例来说是过大的。


查看完整回答
反对 回复 2019-11-06
?
慕的地10843

在这种情况下,反射确实与安全/保障正交。


我们如何限制反射?


Java具有安全管理器,并且ClassLoader是其安全模型的基础。就您而言,我想您需要看看java.lang.reflect.ReflectPermission。


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


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


我们应该担心反射给我们带来的超级大国吗?是的,没有。


是的,因为Java平台应该由Classloader安全管理器保护。破坏反射的能力可以看作是一种突破。


没有在这个意义上,大多数系统都是反正不是完全安全的。很多类经常可以被子类化,您可能已经就这样滥用了系统。当然类可以制成final或密封,使他们不能在其他罐子被继承。但是据此,只有少数几个类得到正确保护(例如String)。


有关最终课程的详细信息,请参见此答案。另请参阅Sami Koivu的博客,以获取有关安全性的更多Java 技巧。


Java的安全性模型在某些方面可以视为不足。诸如NewSpeak之类的某些语言甚至采用了更为激进的模块化方法,在这种方法中,您只能访问依赖关系反转显式提供给您的内容(默认情况下为无)。


同样重要的是要注意,安全性始终是相对的。在语言级别,例如,您不能阻止模块形式消耗100%的CPU或消耗所有内存,直到一个OutOfMemoryException。这些问题需要通过其他方式解决。将来我们可能会看到Java扩展了资源利用率配额,但是明天就不行了:)


我可以在这个问题上做更多的扩展,但是我认为我已经指出了。


查看完整回答
反对 回复 2019-11-06

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信