在我们在 Java Web 应用程序中的视图中,目前我将hashCode
对象用作 Id,以便在服务器端我可以取回相同的对象。
但是,我想知道 Java 到底有多安全hashCode
,以至于有人无法破解它来检索其他人的对象。我不太倾向于加密-解密机制,因为它会占用大量 CPU。
我可以使用哪些其他快速而安全的机制?
在我们在 Java Web 应用程序中的视图中,目前我将hashCode
对象用作 Id,以便在服务器端我可以取回相同的对象。
但是,我想知道 Java 到底有多安全hashCode
,以至于有人无法破解它来检索其他人的对象。我不太倾向于加密-解密机制,因为它会占用大量 CPU。
我可以使用哪些其他快速而安全的机制?
你打破了其中一个hashCode()
禁忌;您将其输出用作关键标识符。那是错误的。
你看,hashCode()
(在其默认实现中)的输出是一个 32 位无符号整数,大约是 40 亿个唯一哈希码。听起来很多?好吧,没有那么多。在这种情况下应用生日问题表明,对于大约 77000 个对象,您有大约 50% 的碰撞机会。两个对象具有相同 hashCode 的概率为 50%。
另一个问题是 的实现hashCode()
可以从一个 Java 版本更改为另一个。它并不意味着是一个对象的永久标识符,因此没有任何东西迫使它在不同版本之间保持一致。
如果您坚持使用散列作为对象标识符,那么最好有自己的方法而不是hashCode()
使用您的密钥标识符(例如,getMySpecialHashKey()
. 您可以使用类似MessageDigest.getInstance("SHA-256")
的方法将对象消化成一个不错的 256 位密钥。
我的建议:放弃散列对象的整个想法,而是在构造对象时为对象生成一个随机标识符。类似于(在Java中)的东西
SecureRandom secRand = new SecureRandom();
byte[] objIdBytes = new byte[16]; //128-bit
secRand.nextBytes(objIdBytes);
String objId = Base64.encodeBase64String(objIdBytes); //Here's your ID
您似乎还将对对象的访问绑定到只知道其键的知识。这也是错误的。需要具有适当身份验证和授权的适当的基于权限的模型。
JavahashCode()
从来没有打算像这样使用。永远不要这样做!一个类的所有实例返回相同的 hashCode 甚至是合法的(虽然不推荐)。Java 中的合约是“两个被认为相等的对象必须具有相同的哈希码”。不多也不少。例如,为所有奇数返回哈希码 1 和为偶数返回 0 是有效的。
hashCode
用于在 Collections 中进行更快的查找,例如预期会HashMap
发生碰撞。
许多类都定义了自己的hashCode()
.
所以,这是绝对不安全的。
它不是加密安全的,因为这不是它的要求的一部分。
hashCode 如何实现是 JVM 的实现细节,并且对于不同的对象类型可能不同(对象可以覆盖 hashCode 方法并提供自己的实现)。
Java hashCode 的目的是在将对象放入哈希表时识别它们。它的主要要求是性能。它的长度也只有 32 位,太短而无法避免冲突。就其预期目的(哈希表)而言,具有相同哈希的两个对象只是一个小问题,但要识别没有歧义冲突的对象是一个大问题。
但是您通常不应允许任何用户仅仅因为他们知道对象的哈希码而访问任何对象。使用适当的权限管理方案。使用受密码保护的用户帐户,其中每个用户都有一组不同的权限参数。当用户试图访问一个对象时,检查它的权限是否允许他们这样做,并在他们不允许时拒绝请求。
对于猜测值可以揭示信息的情况,它肯定不够安全。
面对任意用户选择的输入,至少一些覆盖甚至不够安全,无法用作哈希码(使用 Java 的String.hashCode()
覆盖对任何东西进行哈希 DoS 攻击真的很容易)。
如果您需要一个标识符,我建议使用一个简单的标识符(例如,数据库中的数字键、作为哈希表键的原子递增整数等),然后附加该标识符的加密哈希和盐.
也就是说,您公开的 ID 将采用以下形式(与语言无关的形式):
Concatenate(PrivateID, HexString(SHA256(UTF8Bytes(Concatenate(Salt, PrivateID)))))
(您可以以减少暴力破解的时间为代价删除部分哈希以获得较小的 ID)。
然后可以将私有ID作为子字符串获取,并且可以再次验证完整的ID。通用过程也很容易在语言之间移植。
相对于系统的其余部分而言,这将花费的少量 CPU 周期不会产生很大的影响,除非您的系统是如此微不足道以至于您无论如何都不会使用太多 CPU,所以除非您正在运行它真正低功耗的东西(例如,远不如廉价手机强大)不会成为问题。
如果您实际上想将哈希码用作哈希码并且必须处理用户选择的输入,则使用可种子哈希算法(例如 SpookyHash)并将您的种子基于启动时间(或者更好,混合启动时间和创建哈希容器的时间)。(与加密散列无关,因此它们可以更快;唯一避免的安全风险是散列 DoSing)。