2011-07-05 270 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()爲您的密碼提供簡單且安全的散列,並自動提供哈希值。

我目前沒有投票或評論的能力,這就是爲什麼我提供了一個明確的聲明,以避免混淆。