【问题标题】:Salting my hashes with PHP and MySQL用 PHP 和 MySQL 给我的哈希加盐
【发布时间】:2010-07-18 12:03:41
【问题描述】:

像大多数用户一样,我只是想找出一种安全的方式来存储密码。我在这里没有找到(或者可能是我缺乏理解)是如何在我的数据库中检索加盐哈希并将盐与散列密码分开,尤其是每个密码都有唯一的盐,而将盐+密码保存在单个列中。

我正在寻找所有这些很酷的方法来加密密码(SHA-256,但 MySQL 是否只支持 SHA/1 和 MD5?)以及 PHP 手册中的其他内容,但不确定如何存储和检索密码。

所以,到目前为止,我所了解的就这些了:

SHA('$salt'.'$password') // My query sends the password and salt 
                         // (Should the $salt be a hash itself?)

在那之后我迷失了盐分。

没有盐的情况下检索密码很容易,但是盐让我很困惑。我在哪里可以再次从 $salt 获得价值,特别是如果它是唯一且安全的?我是否将它们隐藏在另一个数据库中?常量(似乎不安全)?

编辑: HMAC 中的关键变量应该是盐还是其他?

【问题讨论】:

  • FWIW,使用 SSL 支持构建的 MySQL 5.5 包括一个提供 SHA224、SHA256、SHA384 和 SHA512 哈希算法的 SHA2() 函数。如果您使用的是早期版本的 MySQL,请使用 PHP 中的hash() 函数。
  • 再次感谢您,Bill,这是您第二次提供帮助!我使用的是 5.1,因为它带有 WAMP (wampserver.com)。我必须卸载我的旧 MySQL 才能安装这个。不幸的是,我不确定如何更新它......我将尝试使用 PHP 进行加密。

标签: php mysql security salt


【解决方案1】:

首先,您的 DBMS (MySQL) 不需要任何对加密哈希的支持。你可以在 PHP 端做所有这些,这也是你应该做的。

如果要将盐和哈希存储在同一列中,则需要将它们连接起来。

// the plaintext password
$password = (string) $_GET['password'];

// you'll want better RNG in reality
// make sure number is 4 chars long
$salt = str_pad((string) rand(1, 1000), 4, '0', STR_PAD_LEFT);

// you may want to use more measures here too
// concatenate hash with salt
$user_password = sha512($password . $salt) . $salt;

现在,如果您想验证密码,请执行以下操作:

// the plaintext password
$password = (string) $_GET['password'];

// the hash from the db
$user_password = $row['user_password'];

// extract the salt
// just cut off the last 4 chars
$salt = substr($user_password, -4);
$hash = substr($user_password, 0, -4);

// verify
if (sha512($password . $salt) == $hash) {
  echo 'match';
}

您可能想看看phpass,它也使用了这种技术。这是一个 PHP 散列解决方案,其中使用了盐分。

您绝对应该看看 WolfOdrade 链接到的问题的答案。

【讨论】:

  • 谢谢!很多新功能,但我可以自己弄清楚细节。这正是我一直在寻找的。我从来不知道减字符串功能,这很有帮助。是否可以从字符串的前面减去不同的盐放置?再次感谢!
  • 当然,substr 可以同时处理这两种情况。如果前面有 4 个字符的盐,你会这样做:$salt = substr($user_password, 0, 4); $hash = substr($user_password, 4);
  • 谢谢。我尝试了 hash_hmac,但在比较登录密码和数据库密码时遇到了问题。 hash_hmac 中的盐是随机生成的,如何减去呢?我在你的例子中也注意到了同样的事情。 sha512($password . $salt) 正在散列密码和唯一的盐。我将如何获取唯一的盐以使用数据库通行证对输入的密码进行哈希处理?抱歉问题过多,我离这个只有几英寸!
  • 好吧,在我的示例中,盐与密码一起存储。这允许您在检查哈希之前拆分两者。 hash_hmac 基本上是一种创建加盐密码的更复杂的方法。在 WolfOdrade 的链接中,他们建议使用由每个用户随机数(如我的 4 个字母盐)和全局应用程序可访问的“站点密钥”组成的盐(因此受损的数据库不会损害盐)。这两个连接将是 hash_hmac 函数的 $key 参数。
  • 每个用户的随机数是我遇到的麻烦。它不是唯一的,但重复将非常罕见。就像您的示例一样,我有一个随机数,但有 10 位数字,并且填充使用随机字符,然后将其作为盐进行散列并添加到 hmac 中。我只是想不通我应该把这些每个用户的盐放在哪里,但在同一列中。在 WoflOrdade 的链接中,他在字符串之间使用了冒号,但我在哈希中找不到任何冒号,所以我看不出它们有什么帮助。
【解决方案2】:

我个人建议让 MySQL 使用其内置函数来执行此操作。

我这样做的方法是在我的数据库配置文件中创建一个返回密钥字符串的函数。配置文件应该在您的站点根目录之外,以便网络服务器可以访问该文件,但不能访问其他文件。比如:

function enc_key(){
     return "aXfDs0DgssATa023GSEpxV";
}

然后在您的脚本中将其与 MySQL 中的 sql 查询以及 AES_ENCRYPT 和 AES_DECRYPT 函数一起使用,如下所示:

require_once('dbconf.inc.php');

$key = enc_key();

//When creating a new user
$sql = "INSERT INTO users (username, password) VALUES ('bob', AES_ENCRYPT('{$key}', {$password}))";

//When retrieving users password
$sql = "SELECT AES_DECRYPT('{$key}', password) AS password FROM users WHERE username like 'bob'";

【讨论】:

  • 这真的不是一个好主意。你不应该加密密码,你应该对它们进行哈希处理(如果有人入侵了你的服务器,他将拥有对所有用户密码的完全访问权限)。您还通过没有转义来暗示 SQL 注入。
  • 首先,我意识到那里的字符串对sql注入是开放的;然而,这不是讨论的主题。 sql 语句只是示例,我假设他们意识到需要清理用户输入。我不是来为他们写申请的,我是来回答具体问题的。
  • 其次,只要您确保密钥安全,2 路加密就与“散列”一样安全。我意识到,如果有人入侵了您的服务器,他们就有了密码,但是如果有人入侵了您的服务器,他们就可以访问所有内容,包括数据库中的所有其他信息,因此散列密码成为一个有争议的问题,您不觉得吗?跨度>
  • 如果有人访问了您的密钥(例如:sql 注入破坏了查询,导致显示失败查询的错误消息)和加密密码,则很容易获得所有明文密码.如果使用了哈希,这是不可能的。
  • 如果您使用输入密码作为加密密钥进行加密,那么两次加密就足够安全了,因为密钥永远不会被存储。
猜你喜欢
  • 1970-01-01
  • 2011-05-27
  • 2014-06-09
  • 1970-01-01
  • 2010-09-28
相关资源
最近更新 更多