秘密可以隐藏在提供访问凭证的“安全”java类中吗?

这是一个头脑风暴的问题,关于Java(或不是)的可能性。 我想知道是否可以隐藏类中的秘密,并防止再使用Java代码或其任何function (安全性,reflection,序列化,类加载器,你的名字……)来访问它。

以下是我到目前为止的想法:

public final class Safe { private String secret; private HashMap validCertificates = new HashMap(); public Safe(String aSecret) { this.secret = aSecret; } public final class Credentials { private String user; private Credentials(String user) { this.user = user; } } public final Credentials getCredential(String user) { // Following test is just for illustrating the intention... if ( "accepted".equals(user) ) { return new Credentials(user); } else { return null; } } public String gimmeTheSecret(Credentials cred) { if ( this.validCertificates.get(cred.user) == cred ) { return secret; } else { return null; } } private void writeObject(ObjectOutputStream stream) throws IOException { throw new RuntimeException("No no no no no no no!!!"); } } 

可以改进吗? 它应该改进吗? 是否无法实现在安全类中锁定秘密的想法?

编辑

关联:

有些人质疑我在这里提出的问题的相关性。 虽然我要问一个普遍的问题,以便触发一个开放的对话,但这个课程有一个非常具体的应用:

  • 如果我想解密某些消息,我需要将私钥数据加载到类中。 如果我无法阻止其他Java代码访问它,则无法创建安全系统。 当然,如果我想要解密一条消息,我宁愿在课堂上这样做而不是泄露秘密,但是,保险箱必须保持牢不可破。

澄清:

  • 该类的实例仅在运行时创建,而不是在编译时创建
  • 代码可以在Web服务器应用程序或任何桌面或设备应用程序中运行
  • 该类仅用于在运行时存储密钥,在内存中,没有计划保留它(对于持久性,可以/应该使用经典加密技术)

事实:

  • 要在Java应用程序中实现安全性,应该设置一个SecurityManager实例,其中根据需要覆盖检查方法
  • 此应用程序可以使用安全类加载器加载不受信任的代码,并为其加载的类分配保护域。 此域不应包含RuntimePermission(“setSecurityManager”)。
  • 不受信任的代码可以尝试更改SecurityManager,但由于Secure Class Loader未授予setSecurityManager权限,因此将抛出SecurityException。

解决的问题:

关于执行环境,我们需要区分两种情况:

  • 受控环境:我们开始使用不受信任的代码尝试打破“安全”的应用程序。

如果我们设置一个适当的SecurityManager禁用reflection并限制任何加载的不受信任代码的权限,那么我们的秘密是安全的。

  • 不受控制的环境:黑客开始使用不受信任的代码试图打破我们的“安全”的应用程序。

黑客可以使用自己的安全管理器和安全类加载器创建自己的应用程序。 它可以从类路径加载我们的代码并执行它,就好像它是我们自己的应用程序一样。 在这种情况下,他可以破坏保险箱。

  • 正如另一个问题所确定的那样,sun.misc.Unsafe无法打破安全管理器

不,它与其他Java代码不安全。 您可以从Safe的实例中检索您的秘密,如下所示:

 Field field = safe.getClass().getDeclaredField("secret"); field.setAccessible(true); String secret = (String) field.get(safe); 

更新:如果您控制要隐藏其他Java代码的加载,则可以使用自定义SecurityManagerClassLoader来阻止对其进行访问。 您需要控制其运行的环境才能工作,例如您限制访问的服务器。

但是,您编辑的问题提到代码可以在任何桌面或设备上运行。 在那种情况下,你真的无法做任何事情来保护秘密免受其他可以做任何事情的进程。 即使你在内存中对它进行加密,另一个进程也可以直接拦截密钥甚至是明文秘密。

如果您不控制环境,那么您需要一些安全的环境,那么您可能需要考虑不同的方法。 也许你可以避免将秘密存储在内存中?

这种“安全”是可笑的。

它在哪里运行? 在我的桌面上? 我使用调试器连接到JVM并以明文forms查看所有秘密。

或者我将我的代码放在它旁边并使用reflection来转储内容。

或者我通过BCEL注入我自己的代码修改,并修改Safe的构造函数以将“secret”值转储到文件中。

或者我只需将整个包替换为我的同名,将其放入bootstrap类加载器中。

或者我甚至可以修改和编译java源代码以获得修改后的JVM。

或者……我可以列出几种从运行时实例中提取值的方法!

任何安全设计中的真正问题是: 谁是攻击者 ? 什么是威胁模型? 没有回答这个问题,这个话题毫无意义。

你可以秘密“难”访问,但你不能让它变得不可能。 有一种说法(我相信Bruce Schneier):对于一个随意的用户,一切都有效。 对一个坚定的cookies没有任何作用。

http://code.google.com/p/joe-e/是java的对象function子集,旨在允许可分解的安全性 – 即使其他部分,程序的一部分也能保留其安全属性的能力该程序受到攻击者的控制,攻击或操纵。

也就是说,允许有效的JVM使用其他语言function扩展语言的语义,例如在运行时附加调试器的能力。 可以使用Runtime调用shell访问的代码可以将调试器附加到许多库存JVM,并解决private访问限制,即使JVM已设置为正常reflection也考虑了字段private性。

Joe-E不允许反复滥用信息隐藏,这可能会使此复杂化,当然也不允许未经过滤的Runtime访问。

我认为你可以做到这一点,但你最终会把安全问题推到别的地方。 是否有任何理由不能使用(为简单起见)您最喜欢的对称密钥算法来加密“秘密”? gimmeTheSecret()方法必须使用另一个参数作为解密秘密的密钥。

当然,问题变得是这个秘密密钥需要被用户或安全地存储在某处的用户或机器输入。 您可以使用某种硬件安全模块,具体取决于数据的敏感程度和您想花多少钱!

有些人质疑我在这里提出的问题的相关性。 虽然我要问一个普遍的问题,以便触发一个开放的对话,但这个课程有一个非常具体的应用:

如果我想解密某些消息,我需要将私钥数据加载到类中。 如果我无法阻止其他Java代码访问它,则无法创建安全系统。 当然,如果我想要解密一条消息,我宁愿在课堂上这样做而不是泄露秘密,但是,保险箱必须保持牢不可破。

首先是安全性,不存在牢不可破。 通过随机我只需在键盘上随机写东西就可以找到你的铭文密钥。 如果你的钥匙很复杂会让你成为一个非常不走运的人,但这是可能的。

第二点,使用公钥/私钥值对来保护客户端和服务器之间的通信是非常常见的,如果做得对,它可以完美地工作。 但必须明白,这确实保护了两台计算机之间的通信。 它不能保护计算机本身。 整个过程基于计算机对彼此的完全信任。

第三点,当你对计算机进行物理访问时 ,你可以用它做任何你想做的事情,特别是这包括窥探程序所做的一切。 以及计算机的所有内容。 内容可以加密是,但在使用时,它不再加密。 这是公钥/私钥系统的一个主要问题:私钥存储在某处,因此您必须确保这个地方是安全的。

如果您信任通信中涉及的计算机,则可以完全接受此流程。 例如,当您连接到您的银行帐户时就是这种情况。

银行信任银行计算机,并且提供的外部词访问权限受到限制和控制。 他们是“安全的”。

如果您将自己的私钥或访问凭证提供给银行。 您受到了损害,但这是您的责任和问题。 因为你的利益不会受到损害,所以你会尽力避免这种情况。 这很好,因为你是一个完全控制你的计算机的人。

但是,假设您从公共计算机或其他人的计算机访问您的银行。 然后一个简单的键盘记录器可以记录您输入密码时所做的键和鼠标移动。

客户端的安全性基于您可以在客户端上拥有的信任。 如果你可以相信他,这是完美的,它的工作。 如果你做不到,那就破了。

如果需要运行不受信任的代码,最好的方法是在单独的JVM中运行它。 这样,不受信任的代码可以被赋予最严格的限制,甚至可以被杀死,例如,如果您有一个逃离CPU或崩溃JVM。 代码可以进行的唯一访问是通过您提供的方式使用reflection,无论您做什么,都不会让您访问另一个JVM中的类。

您甚至可以为您的应用构建监狱或迷宫。 这可以允许不受信任的代码在看似真实的系统中运行,但实际上并非如此。 (其目的是绑定你任何人将是黑客足够长的时间,看看他们在做什么)

您甚至可以在自己的虚拟机中运行JVM,因此看起来您有完整的(但是虚拟的)系统来“入侵”。 可以保存虚拟机以进行分析并非常容易地擦除到预设状态。

最终的解决方案是将不受信任的代码放在自己的虚拟LAN上。 (他们用来分析stuxnet蠕虫的东西。;)

不,你不能以这种方式在课堂上嵌入秘密。 如果你编译了那么,我可以在生成的类文件上运行strings ,它可能在那里。

如果您只是尝试validation它,那么您可以采取的一种方法是在其中存储哈希值。 一旦你想validation输入,哈希并比较哈希。

您可以使用proguard或类似工具从.class文件中对其进行模糊处理。 您也可以签署您的JAR,以便其他软件包无法访问它。 签署您的JAR也可能有所帮助。

假设传递给方法调用的信息是安全的,那么密钥就是一个很好的解决方案。 密钥不需要存储在应用程序的任何位置,因此,不能仅通过Java访问信息。 如果你想要一种方法与其他人分享秘密而不给他们你的密钥,那就很有趣了,这就是下面的shareSecret方法。 然而,管理它变得棘手。 一个过程可能是:

1)秘密搜索者请求访问,输入存储的临时密钥

2)秘密管理员使用其密钥授予访问权限,删除临时密钥,并创建适用于临时密钥的临时安全对象。

3)秘密搜索者输入临时密钥和永久密钥,删除临时安全对象,并创建一个可以使用永久密钥访问的新永久安全对象。

同样,假设传递给方法调用的参数是安全的,上述过程的主要问题是有人可能已经劫持了1和2之间的临时密钥并使用它来查看步骤2和3之间的临时秘密。但是,它会使比将其存储在纯文本字符串中更难以破解。

 public final class Safe { private String secret; public Safe(String secret, String key){ this.secret = encode(secret, key} public String getSecret(String key){ return decode(this.secret, credentials); } public Safe shareSecret(String fromKey, String toKey){ return new Safe(decode(this.secret, fromKey), toKey); } private String encode(String secret, String key){ //Code to encode the secret based on key here... } private String decode(String secret, String key){ //Code to decode the secret based on key here... } } 

这个问题还有另一个角度:Java提供了权限。 在控制环境的上下文中,可以为加载了安全类加载器的类分配一组权限。

Java提供了与安全性实现相关的3个权限对象:

PrivateCredentialPermission SecurityPermission AuthPermission

在实施加密系统时,这些可以帮助改进和控制对function的访问。 它们必然足以使系统安全。

为偶然发现这个旧线程的其他人添加评论….

有时你需要的只是一个’签名’值,而不是’加密’值。 例如,(编码的)许可证令牌需要签名,但不需要加密。 在这些情况下,您可以分发PUBLIC密钥,这不是任何人都可以看到它的问题。 您仍然受到保护,因为其他人无法创建签名令牌。

您还可以使用公钥将加密邮件发送到服务器。

当然,这不会阻止知识渊博且坚定的攻击者 – 请参阅其他回复。 在这种情况下,有人可以简单地用自己的公钥代替你的公钥。 如果您不validation证书链。 但是,如果您可以使用签名而不是加密,它将为您节省大量的麻烦。