【问题标题】:Why Spring Security's BCrypt implementation uses a no-early-return equals method?为什么 Spring Security 的 BCrypt 实现使用不提前返回的 equals 方法?
【发布时间】:2016-03-17 15:33:08
【问题描述】:

在 Spring Security 的 BCrypt 实现中,我遇到了以下方法(source):

static boolean equalsNoEarlyReturn(String a, String b) {
  char[] caa = a.toCharArray();
  char[] cab = b.toCharArray();

  if (caa.length != cab.length) {
    return false;
  }

  byte ret = 0;
  for (int i = 0; i < caa.length; i++) {
    ret |= caa[i] ^ cab[i];
  }
  return ret == 0;
}

为什么使用这种 no-early-return equals 方法?它有什么好处:

static boolean equals(String a, String b) {
  char[] caa = a.toCharArray();
  char[] cab = b.toCharArray();

  if (caa.length != cab.length) {
    return false;
  }

  for (int i = 0; i < caa.length; i++) {
    if (caa[i] != cab[i]) {
      return false;
    }
  }
  return true;
}

甚至:

return a.equals(b)

?

是出于安全的考虑吗?

【问题讨论】:

    标签: java spring spring-security equals bcrypt


    【解决方案1】:

    我认为这是出于安全目的,以避免定时攻击。即使它与 BCrypt 并不真正相关,这样做也是一种很好的做法。 Here is an answer 了解更多信息

    【讨论】:

    • 请考虑您不知道正确的密钥。使用提前退出,您可以首先通过检查执行时间来确定密钥长度。增加密钥长度,直到执行长度增加。太好了,你已经完成了第一步。然后,您可以为密钥的每个步骤执行此操作。以 a 开始,以 0 结束。其中一个需要更长的时间,因为它必须额外执行下一个键值(因为第一次匹配)。太好了,你找到了一个匹配的。重复下一个。等等。
    【解决方案2】:

    不知道是不是真的原因,但是在original article我找到了

    key 参数是一个秘密加密密钥,它可以是用户选择的最多 56 个字节的密码(当密钥是 ASCII 字符串时,包括一个终止零字节)。

    由于byte 是8 位,char 是16 位,ret |= caa[i] ^ cab[i]; 仅用于比较最低有效位。

    我试图执行

    final char a = '\uff11';
    final char b = '\u1111';
    
    byte result = 0;
    result |= a ^ b;
    

    result0

    我认为原因可能是只接受 ASCII 密码,或者 UTF 8(或其他字符集)被视为 ASCII 字符。

    【讨论】:

    • 1.原因是为了防止定时攻击。如果攻击者尝试密码并获得更长的延迟,他知道他已经猜到密码的一部分,然后保留它并更改下一个字符。定时攻击是真实的,并在许多地方使用。 2. ? 不是 16 位而是一个字符。 3. bcrypt 的开发者是密码专家,他们没有犯过丛林联盟的错误。 4. 你有很多东西要学,Grasshopper
    • @zaph 是的,我是来学习的!非常感谢! (顺便说一句,java中的char是16位的,|^ints之间的按位运算,该运算只比较字符的最低部分)
    • 请注意,bcrypt 格式仅包含 ASCII 字符,除了包含 $ 和 ASCII 字母的前缀之外,其余的都是 Base64 编码的,也都是 ASCII。 ASCII 只使用了低 7 位,所以这就是所有需要比较的地方。这是正在比较的哈希值。事情是这样的:当您遇到看似错误但被广泛接受的事情时,请尝试找出误解。
    • @zaph 是的,现在我明白了,我错过了两个字符串以 base64 编码到达该方法,现在很清楚了。当然我从来没有想过这是错的,只是想为什么会这样实现,非常感谢清晰的解释
    猜你喜欢
    • 2010-09-25
    • 1970-01-01
    • 2015-06-26
    • 1970-01-01
    • 2020-04-11
    • 2014-11-20
    • 1970-01-01
    • 2014-10-12
    相关资源
    最近更新 更多