私有方法真的安全吗?

92

在Java中,private访问修饰符被视为安全的,因为它在类外部不可见。这意味着外部世界也不知道该方法的存在。

但是我认为Java反射可以用来打破这个规则。考虑以下情况:

public class ProtectedPrivacy{

  private String getInfo(){
     return "confidential"; 
  }

}  

现在我将从另一个类中获取信息:

public class BreakPrivacy{

   public static void main(String[] args) throws Exception {
       ProtectedPrivacy protectedPrivacy = new ProtectedPrivacy();
       Method method = protectedPrivacy.getClass().getDeclaredMethod("getInfo", null);
       method.setAccessible(true);
       Object result = method.invoke(protectedPrivacy);
       System.out.println(result.toString());
   }
} 

此时我认为私有方法仍然是安全的,因为要执行上述操作,我们必须知道方法名称。但是,如果包含私有方法的类是由他人编写的,我们就无法访问这些方法。

但由于下面这行代码,我的观点变得无效了。

Method method[] = new ProtectedPrivacy().getClass().getDeclaredMethods();

现在这个method[]包含了完成上述任务的所有内容。我的问题是,有没有一种方法可以使用Java反射来避免做这种事情?

我从Java文档中摘取了一些要点来澄清我的问题。

 

选择访问级别的提示:

   

如果其他程序员使用您的类,则希望确保不会发生因误用而导致的错误。访问级别可以帮助您实现此目标。对于特定成员,请使用最严格的访问级别,这样能够使其有意义。私有(private)是默认的访问级别,除非您有足够好的理由不采用它。


1
使用混淆器可能会有所帮助,因为 getDeclaredMethods 将返回看起来像垃圾的名称。 - Sergey Kalinichenko
214
私有、受保护和公共等访问修饰符并非用于安全性,而是为了避免好心的人犯错误。请勿将其用作安全措施。 - Richard Tingle
20
无论如何,Java 代码都是可反编译的。一个“攻击者”可以下载 Java 反编译器来读取你的代码,或将“私有”的属性更改为“公共”的属性。 - 11684
7
为了详细说明@RichardTingle的评论(顺便点个赞+1)。安全和安全性之间存在着重要的区别。后者是通过访问修饰符来减轻问题,以避免“意外”破坏事物的问题。 - user
3
对于没有访问修饰符的编程语言,比如Python,这一切对程序员来说都非常清楚。在这些语言中,private/public只是通过名称约定进行区分(在Python中,任何以单个下划线开头的内容应被视为私有)。这使得明确表明某些内容为“私有”并不会增加任何安全性,而只是一种简单的方式来告诉人们应该使用你的API的哪些部分。我怀疑编译器检查这些东西是否真的有帮助,因为遵循约定并不需要“外部”帮助。 - Bakuriu
显示剩余3条评论
7个回答

95

这要看你所谓的“安全”是什么意思。如果你的运行环境使用了允许进行此类操作的安全管理器,那么使用反射API可以做出各种危险的事情。但是在这种情况下,该库很可能可以被修改以使方法变为public。

在这样的环境中,访问控制实际上是“劝告性质”的——你实际上是信任代码能够正常运行。如果你不信任正在运行的代码,应该使用更严格的安全管理器。


3
可以。在这个意义上,更加严格的管理人员指的是对于某个特定事项或决策采取更为保守和谨慎的管理人员。 - Gray
12
@Gray:这是一个抛出相关check*调用异常的SecurityManager实例。 - Jon Skeet

40

访问修饰符与安全无关。实际上,您可以(并且应该)将访问修饰符视为安全的反向 - 它不是为了保护您的数据或算法,而是为了保护人们免于了解您的数据和算法的要求。这就是为什么默认修饰符是包级别 - 如果他们正在处理包,他们可能已经需要知道了。

随着对代码数据和方法的了解,也有使用它的责任和义务。您不会在inIt方法上放置私有标记以防止某人发现它,而是因为(a)他们不会知道您仅在foo之后调用它,并仅在bar = 3.1415时才这样做;(b)因为知道它对他们没有好处。

访问修饰符可以用一个简单的短语概括:“TMI,伙计,我< strong >真的不需要知道”。


4
回答很好,但我需要谷歌一下才知道TMI是指太多信息。我猜我需要在硅谷待更长时间 :) Translated: 回答很棒,但我需要谷歌一下才知道TMI是什么意思——它表示“太多信息”。我想我需要在硅谷待更久一些 :) - mikelong

7
通过说“安全”,你保护你或其他开发人员,他们使用你的API时不会通过调用你的私有方法来损害对象。但是如果你或他们真的需要调用这个方法,可以使用反射来实现。

6
问题是你试图从谁那里“拯救”它。在我看来,这样一个使用你代码的客户端是处于劣势的。
任何一段(由你或他人编写)试图访问上述类的私有成员的代码实际上都是自取灭亡。 私有成员不属于公共API,并且可能会在没有通知的情况下发生变化。 如果客户端以上述方式使用其中的一个私有成员,则在升级到修改了私有成员的新版本API时,代码将会崩溃。

5

有了便利,就要有责任。有一些事情你不能做,和一些你可以做但你不应该做。

私有(Private)修饰符被提供/用作最受限制的方式。应当定义为私有的成员在类外部不应可见。但是,我们可以通过反射来打破这个限制。但这并不意味着你不应该使用私有修饰符或它们是不安全的。重点是要明智地或以建设性的方式使用它们(如反射)。


5
假设您信任API的客户端程序员,另一个考虑的方式是他们使用这些特定函数的“安全性”。
您公开可用的函数应该提供清晰、文档完备、接口稳定的代码。您的私有函数可以被视为实现细节,并且可能随着时间的推移而发生变化,因此不适合直接使用。
如果客户端程序员费尽心思地规避这些抽象层,则可以认为他们知道自己在做什么。更重要的是,他们明白这是不受支持的,并且可能无法与您的代码的未来版本一起使用。

5

private并不是为了安全,而是为了代码整洁和避免错误。它使用户能够将代码模块化(以及如何开发),而无需担心其他模块的所有详细信息。

一旦你发布代码,人们就可以弄清楚它的工作原理。如果你最终想让代码在计算机上运行,那么就没有办法“隐藏”逻辑。即使编译成二进制也只是一种混淆的程度。

因此,你无法设置API来执行你不希望别人调用的特殊操作。对于Web API,你可以将想要控制的方法放在服务器端。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接