代码之家  ›  专栏  ›  技术社区  ›  polygenelubricants

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

  •  96
  • polygenelubricants  · 技术社区  · 15 年前

    我越来越了解 java.lang.reflect.AccessibleObject.setAccessible ,我对它所能做的就越惊讶。这是根据我对这个问题的回答改编的( Using reflection to change static final File.separatorChar for unit testing )

    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 可以,但必须承认它有合法的用途来提供它。所以我的问题是:

    • 真正合法的用途是什么 可设置 ?
      • Java最初是否被设计成没有这个需求?
      • 这种设计的负面后果(如果有的话)是什么?
    • 你能限制吗 可设置 仅用于合法用途?
      • 只有通过吗 SecurityManager ?
        • 它是如何工作的?白名单/黑名单、粒度等?
        • 在应用程序中配置它是常见的吗?
      • 我能把我的课程写成 可设置 -不管怎样证明 安全管理器 配置?
        • 或者我是谁来管理配置的?

    我想还有一个更重要的问题:我需要担心这个吗????

    我所有的课程都没有任何可强制执行的隐私。单例模式(抛开对其优点的怀疑)现在不可能实施。正如上面的片段所示,甚至一些基本的Java基本工作的假设甚至没有被保证。

    这些问题不是真的吗????


    好吧,我刚确认:多亏了 可设置 ,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!"
       }
    }
    

    难道只有我一个人认为这是一个大问题吗?

    3 回复  |  直到 6 年前
        1
  •  102
  •   Sami Koivu    15 年前

    我需要担心这个吗????

    这完全取决于你所编写的程序类型和架构类型。

    如果你正在向全世界的人分发一个名为foo.jar的软件组件,不管怎样,你完全受他们的摆布。它们可以修改.jar中的类定义(通过反向工程或直接字节码操作)。他们可以在自己的jvm中运行您的代码,等等。在这种情况下,担心对您没有好处。

    如果您编写的web应用程序只通过http与人和系统进行接口,并且您控制应用程序服务器,那么也不必担心。当然,你公司的其他程序员可能会创造出破坏你的单例模式的代码,但前提是他们真的想这样做。

    如果您未来的工作是在Sun MyStase/Oracle编写代码,您的任务是编写Java核心或其他受信任组件的代码,这是您应该意识到的。然而,担心只会让你失去头发。无论如何他们可能会让你读 Secure Coding Guidelines 以及内部文件。

    如果你要编写Java applet,安全框架是你应该意识到的。您会发现尝试调用setaccessible的未签名小程序只会导致securityexception。

    可设置性并不是常规完整性检查的唯一问题。有一个非API,核心Java类,叫做Sun.MISC.UnStore,它几乎可以做任何它想做的事情,包括直接访问内存。本地代码(jni)也可以绕过这种控制。

    在沙箱环境(例如Java applet、JavaFX)中,每个类都有一组权限,对不安全、可访问和定义的本机实现的访问由SuffigMyM管司控制。

    “Java访问修饰符不是安全机制。”

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

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

    Java核心类使用它作为一种简单的方法来访问出于安全原因而必须保持私有的内容。例如,Java序列化框架使用它在调用对象时调用私有对象构造函数。有人提到system.seterr,这是一个很好的例子,但奇怪的是,系统类方法setout/seterr/setin都使用本机代码来设置最后一个字段的值。

    另一个显而易见的合法用途是需要窥视对象内部的框架(持久性、web框架、注入)。

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

    Java最初是否被设计成没有这个需求?

    这个问题很难回答。我想是的,但是你需要添加一些其他的机制,这些机制可能不太适合。

    您能否仅将setaccessible限制为合法使用?

    您可以应用的最直接的ootb限制是有一个securitymanager并允许setaccessible只对来自特定源的代码进行访问。这就是Java已经做的事情——来自JavaJHOLD的标准Java类允许做可访问的,而Foo.com上的无符号applet类不允许做可访问的。如前所述,这个许可是二元的,也就是说一个人有没有它。没有明显的方法允许setaccessible修改某些字段/方法,而不允许其他字段/方法。但是,使用securitymanager,您可以禁止类完全引用某些包,无论是否有反射。

    我是否可以将我的类编写为可设置的证明,而不考虑securitymanager配置?…或者我是谁来管理配置的?

    你不能而且你肯定是。

        2
  •  14
  •   Paul Bellora    12 年前
    • 真正合法的用途是什么 setAccessible ?

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

    • Java最初是否被设计成没有这个需求?
    • 这种设计的负面后果(如果有的话)是什么?

    很多事情都是不可能的。例如,各种Java持久性、序列化和依赖注入都依赖于反射。几乎所有在运行时依赖javabeans约定的东西。

    • 你能限制吗 可设置 仅用于合法用途?
    • 只有通过吗 SecurityManager ?

    对。

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

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

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

    不。

    • 我能把我的课程写成 可设置 -不管怎样证明 安全管理器 配置?
      • 或者我是谁来管理配置的?

    不,你不能,是的。

    另一种选择是通过源代码分析工具“强制”执行;例如自定义 pmd findbugs 规则。或对(例如)标识的代码进行选择性代码审查 grep setAccessible ... .

    作为对后续行动的回应

    我所有的课程都没有任何可强制执行的隐私。单例模式(抛开对其优点的怀疑)现在不可能实施。

    如果这让你担心,那么我想你需要担心。但你真的不应该试着 其他程序员要尊重你的设计决策。如果他们愚蠢到无缘无故地为你的单例创建多个实例(例如),他们可以承受后果。

    如果你的意思是“隐私”包含保护敏感信息不被泄露的意思,那你也找错了方向。保护敏感数据的方法是不允许不受信任的代码进入处理敏感数据的安全沙箱。Java访问修饰符不是安全机制。

    <字符串示例-是否只有我认为这是一个重大问题?

    可能不是 只有 一:但我不担心。不受信任的代码应该在沙箱中执行,这是公认的事实。如果你有可信的代码/可信的程序员做这样的事情,那么你的问题比拥有意外的可变字符串更严重。(想想逻辑炸弹……)

        3
  •  11
  •   Community CDub    8 年前

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

    我们如何限制反射?

    Java有安全管理器 ClassLoader 作为其安全模型的基础。对你来说,我想你应该看看 java.lang.reflect.ReflectPermission .

    但这并不能完全解决反思的问题。可用的反射功能应该受到细粒度授权方案的约束,而现在不是这样。例如,允许某些框架使用反射(例如hibernate),但不允许使用其他代码。或者允许程序只以只读方式反映,以便调试。

    未来可能成为主流的一种方法是使用所谓的 镜子 将反射能力从类中分离出来。见 Mirrors: Design Principles for Meta-level Facilities . 但是有很多种 other research 这解决了这个问题。但我同意,对于动态语言来说,这个问题比静态语言更严重。

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

    是的 在这个意义上,Java平台应该是安全的。 Classloader 还有保安经理。干扰反射的能力可以看作是一种突破。

    从这个意义上说,大多数系统无论如何都不是完全安全的。很多类经常可以被子类化,你可能已经用它滥用了系统。当然可以上课 final sealed 所以它们不能在其他jar中被子类化。但根据这一点,只有少数类(例如string)得到了正确的保护。

    看到这个 answer about final class 为了一个好的解释。也可以查看来自 Sami Koivu 更多关于安全的Java黑客攻击。

    Java的安全模型可以被看作是不够的。一些语言如 NewSpeak 对模块化采取更激进的方法,在模块化中,您只能访问依赖性反转(默认情况下什么都没有)显式提供给您的内容。

    同样重要的是要注意安全性 相对的 . 例如,在语言级别,您不能阻止模块消耗100%的CPU或消耗所有的内存,直到 OutOfMemoryException . 这种关切需要通过其他手段加以解决。将来我们可能会看到Java扩展了资源利用率配额,但不是为了明天:)

    我可以在这个问题上多说些,但我想我已经表明了我的观点。