1

我正在使用此代码来加密和解密密码

public class SecureDigester
{
    private static final char digits[] =
           { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                 'F' };

           private static String byteArrayToHexString(byte[] b)
           {
              StringBuffer hexString = new StringBuffer(b.length);
              for (int i = 0; i < b.length; i++)
              {
                 hexString.append(digits[(b[i] & 0xF0) >> 4]);
                 hexString.append(digits[b[i] & 0x0F]);
              }
              return hexString.toString();
           }

           public static String digest(String plaintext)
           {
              try
              {
                 MessageDigest md = MessageDigest.getInstance("SHA");
                 md.update(plaintext.getBytes("UTF-8"));
                 byte[] mdBytes = md.digest();
                 String hashString = byteArrayToHexString(mdBytes);
                 return hashString;
              } catch (Exception e)
              {
                 throw new RuntimeException(e);
              }
           }

}

在我的登录中,我使用此代码来解密密码:

        String passwordDigest = SecureDigester.digest(password);
        if (!user.getPassword().equals(passwordDigest))
        {
            // authentication failed: bad password
        }

现在我有了 forgot-password.jsp 页面,该页面将用户的用户名和密码发送到他/她指定的电子邮件。但是当我使用下面的代码时,我收到了一个加密密码,它也与我数据库中的加密密码不同。

String Email = req.getParameter("email");
User userItem = new UserDAO().findEmail(Email);
SendMailSSL sendEmail = new SendMailSSL();
String password = userItem.getPassword();
String EPassword = SecureDigester.digest(password);
sendEmail.send(userItem.getUsername(), EPassword, userItem.getEmail());

如何解决这个问题?

4

2 回答 2

4

SHA 和 MD5 等加密哈希是单向哈希。无法反转单向哈希。您可以从任何明文生成加密散列,但您无法确定仅给定散列值的原始明文。

您的“解密”代码实际上并未解密 SHA 哈希。相反,它对用户刚刚输入的密码进行哈希处理,并将该哈希值与之前存储的哈希值进行比较。如果哈希匹配,则意味着密码匹配。

像这样散列密码是一种很好的安全方案,但它的一个后果是您无法通过电子邮件向用户发送密码:您没有他们的密码!你只有不可逆的哈希值。这就是为什么如今安全性高的网站如果忘记了原始密码,就不会通过电子邮件将其发送给您。相反,它们提供了一些方法来重置您的密码。如果您遇到一个能够通过电子邮件将您的密码发送给您的网站,那就是一个巨大的危险信号

于 2013-01-29T15:49:12.927 回答
1

看起来您将密码作为 SHA1 哈希值存储在数据库中。这很棒,你应该这样做(绝​​对是最佳实践)。使用 SHA1(或任何其他单向哈希)的“缺点”是您无法将哈希“解密”回原始明文。这意味着以下代码不起作用。

String Email = req.getParameter("email");
User userItem = new UserDAO().findEmail(Email);
SendMailSSL sendEmail = new SendMailSSL();
String password = userItem.getPassword(); // the userItem returns an SHA1 hash
String EPassword = SecureDigester.digest(password); // this just rehashes the hash
sendEmail.send(userItem.getUsername(), EPassword, userItem.getEmail());

问题不在于此代码,而在于方法。由于您没有任何方法可以从 SHA1 哈希中恢复用户的明文密码(这是一个很好的),因此您需要向用户发送重置密码的链接,而不是包含密码的电子邮件。电子邮件是一个不安全的渠道,密码(除了必须更改的初始密码)绝不应通过电子邮件发送。

这个问题有两种方法。

  • 从随机乱码中为用户生成一个新密码,然后对其进行 SHA1 哈希处理。将此值存储在数据库中。将新的随机密码发送给用户并强制用户在下次登录时更改密码。
  • 生成用户可用于重置密码的链接。链接需要难以猜测并在一定时间后过期(即在向用户发送电子邮件后的 5-10 分钟内)。这使得攻击者很难利用“重置窗口”。重置链接每次都应该是随机的,并且尽可能不包含有关用户的识别信息。
于 2013-01-29T15:50:09.240 回答