2011-07-05 44 views
33

我有一个小型社区网站,我需要实现某种被遗忘的密码功能。我目前将密码存储在数据库中,并使用MD5加密。PHP忘记密码功能

是否有可能对“解密”进行排序并通过电子邮件发送给他们,还是需要密码重置页面?

+5

存储密码MD5编码的整个想法是,你不能解码它们。即使有人闯入你的系统并转储数据库,他们也无法获得密码(除非密码当然是弱的,但那是另一回事......) –

回答

127

MD5哈希密码不可逆。 (MD5是哈希,而不是真正的加密,所以有一个细微的差别)。而是的你一定要提供一个密码“重置”的过程(而不是简单地通过电子邮件发送密码)。

为了给你一个安全的密码重置一个高层次的工作流程...

  1. 当用户要求重设密码,让他们进入他们的电子邮件地址
  2. 不表明如果该电子邮件地址是否有效(只要告诉他们电子邮件已发送)。这是开放的讨论,因为它降低了可用性(即我不知道我注册了哪个电子邮件),但它提供的信息较少,试图收集哪些电子邮件实际上在您的网站上注册的信息。
  3. 生成一个令牌(也许用一个salt对一个时间戳进行散列)并将其存储到用户记录的数据库中。
  4. 发送电子邮件给用户,并附上指向您的http s重置页面(URL中的令牌和电子邮件地址)的链接。
  5. 使用令牌和电子邮件地址验证用户。
  6. 让他们选择一个新密码,替换旧密码。
  7. 此外,在某个特定时间段(通常为24小时)之后过期这些令牌是一个好主意。
  8. (可选)记录发生了多少次“忘记”尝试,并且可能在人们请求大量电子邮件时实现更复杂的功能。
  9. (可选)在单独的表中记录请求重置的个人的IP地址。从该IP增加计数。如果它达到的数量超过了10个,......忽略他们未来的要求。

为了让您更详细一点到哈希...

当你像哈希使用MD5(密码的值)的PHP函数,最终值将是相同的无论您在哪个服务器上运行该密码。 (所以我们可以在哈希和加密之间立即看到一个区别......没有涉及私钥/公钥)。

所以这就是你会看到人们提到的一个弱点rainbow tables。彩虹表的一个非常基本的解释是...你md5()散列一串字典单词(弱密码),以便得到它们的md5()哈希值。把这些放在数据库表(彩虹表)中。

现在,如果你攻击一个网站的数据库,你可以对你的彩虹表运行用户的哈希密码(实质)‘反向’的哈希回密码。 (你并不是真正“颠倒”哈希......但你明白了)。

这就是最好的做法,“腌制”你的密码。这意味着(再次,这里非常基本的想法),你在散列它之前在用户的密码之前附加一个随机值。现在,当彩虹表对数据库运行时,由于“密码”的md5()散列与“password384746”不同,所以不容易“反转”。

这是一个很好的SO Q/A,应该有所帮助。 Secure hash and salt for PHP passwords

+1

Thankyou Jared,真的很丰富! – Liam

+1

@Jared Cobb:你的意思是什么8。 – testing

+2

@testing好问题......除了在这种情况下攻击者使用bot网络,代理或其他循环方法每次从不同的IP地址击中你之外,它与第9点非常相似。例如,如果您的系统在几分钟或几小时内检测到“忘记请求”的大幅增加,您可能会考虑暂时放缓或禁用该功能。 (当然,您的门槛将取决于您对您的用户群规模以及“正常”体积的主观判断)。 –

2

MD5旨在成为单向散列。您需要让他们重置密码。

1

不,你不能解密它。这是整个想法。

您需要向他们发送临时密码并让他们重置密码。

1

你需要做一个密码重置页面。 PHP无法解密MD5。

7

不,MD5是不可逆的。散列密码的重点在于让访问数据库的攻击者无法访问每个人的密码。

这就是说,MD5(特别是无盐MD5)通常可以使用rainbow table被攻击。为了安全起见,你最好使用bcrypt

3

你不能解密密码,你甚至不应该考虑通过明文向用户发送密码。 (这是让我永远不会再使用站点的第一种方式;它是一个GIGANTIC安全漏洞。)提供一个密码重置页面,该页面由包含与时间相关的密钥的链接触发,该密钥发送到用户的密码恢复电子邮件;这是密码恢复技术的当前状态。

1

MD5是一种单向函数。你不能解密它。所以你需要有一个密码重置页面。

2

编写接受MD5和电子邮件地址为获取paramaeter并在db的电子邮件和密码md5'd页面。遵循贾里德科布笔记,这应该让你在正确的道路上。我只是增加了一些例子,以及

例如URL发送http://yourdomain.com/resetpassword.php?code=md5codesentviaemail

$code = isset($_GET['code']) ? $_GET['code'] : ''; 
    $email = isset($_GET['email']) ? $_GET['email'] : ''; 
$checkPw = ''; 

    if(empty($code) || empty($email)) 
    { 
    die(); 
    } 
    $sqlQuery = 'SELECT * FROM users WHERE email = "'.$email.'"; 
//remember to check for sql injections 
    //then get the results as an array, i use a database class eg $user 

    if(!empty($user['password'])) 
    { 
    $checkPw = md5($user['password']); 
    }else 
    { 
    die(); 
    } 

    if($checkPw !== $code) 
    { 
    die(); 
    }else 
    { 
    //display form for user to change password 
    } 

这应该是足以让你知道用户是有效用户,并改变他的密码

3

的最好的事情你要做的是请求人在注册时提交他们的电子邮件地址。然后,如果他们忘记了,请忘记密码链接,将密码重置为一个随机值,然后通过电子邮件发送给他们,以便他们获得访问权限,然后将密码更改为更难忘的内容。这样你就不需要损害安全性。 你可以有一个链接,他们只需要提交他们的用户名,但为了更好的安全性,你应该有一个问题和答案或难忘的字。

8

根据这个帖子The definitive guide to forms based website authentication,用于步骤3和4,我不知道,你应该送你存储同样的道理。

我猜你必须发送令牌,然后散列并存储在数据库散列令牌。否则,如果您的数据库受到攻击,可以访问重置密码页面。

总结:

$token = md5(microtime (TRUE)*100000); 
$tokenToSendInMail = $token; 
$tokenToStoreInDB = hash($token); 

其中哈希散列算法。

+1

这是一个很好的观点,将散列标记存储在数据库中。 +1 – Francodi

+1

尽管使用可预测的令牌并不是一个好主意......它不应该基于任何可预测的事情,比如时间。 (这将允许攻击者在生成令牌时迭代值)。在PHP7中,应该使用'random_bytes'。在PHP5上,应该使用兼容性实现或'openssl_random_pseudo_byte'。 –

+1

(另一个选择是使用一个秘密随机字符串和用户的细节的散列,并希望有一些像样的随机数据。因为它存储在数据库中,所以令牌无法预测)(使用这种方法,我会在长字符串上使用'password_hash'来生成令牌,并在链接中发送一个base64编码的哈希版本。 (在存储到数据库之前再次散列它也可能是一个选项,但在4-24小时后过期重置链接可能是更好的防御)(使用适当的随机数据,只需发送base64编码的数据应该没问题) –

2

使用php内置的password_verify和password_hash。

3

正如马库斯里德所言,在2015/2016年,如果您的PHP版本> = 5.5,请勿使用MD5,password_hash()和password_verify()为您的密码提供简单且安全的散列,并自动提供哈希值。

我目前没有投票或评论的能力,这就是为什么我提供了一个明确的声明,以避免混淆。