2010-12-15 90 views
2

再次尋求一些有關PHP安全性和登錄系統的幫助。想知道我是否在這裏正確做到了這一點。如果我沒有足夠的具體足夠的地方請詢問,任何幫助非常感謝。我正在嘗試創建一個安全的登錄系統,僅用於學習目的。這裏是代碼:這是否使用PDO並準備好了用於安全登錄的語句?

require("constants.php"); 
$DBH = new mysqli($dbhost, $dbuser, $dbpass, $dbname); 

function createSalt() { 
    $length = mt_rand(64, 128); 
    $salt = ''; 
    for ($i = 0; $i < $length; $i++) { 
     $salt .= chr(mt_rand(33, 255)); 
    } 
    return $salt; 
} 
//Salt function created by ircmaxell 

function registerNewUser() { 
    //Check to see if  Username Is In Use// 
    $q = $DBH->prepare("SELECT id FROM users WHERE username = ?"); 
    $username = filter_var($username, FILTER_SANITIZE_STRING); 
    $data = array($username); 
    $q->execute($data); 
    $row = $q->fetch(); 

    if ($row === false) { 
     //If Username Is Not Already In Use Insert Data// 
     $hash = hash('sha256', $pass); 
     $salt = createSalt(); 
     $hash = hash('sha256', $salt . $hash . $pass); //UPDATED 
     $data = array($username, $hash, $salt); 
     $qInsert = $DBH->prepare(
      "INSERT INTO users (username, password, salt) values (?, ?, ?)" 
     ); 
     $qInsert->execute($data); //Inserts User Data Into Table// 
    } 
} 
+0

搜索PHP.net, t找到一個名爲「canonicalize()」的函數這是一個用戶定義函數沒有包含在代碼中? – 2010-12-15 19:12:07

+1

另外,bind_param()以「i」作爲變量被調用,但是「i」沒有在您準備好的語句中定義。您可以將用戶名變量作爲單個成員數組傳遞給execute語句並跳過bind_param()。 – 2010-12-15 19:16:42

+0

是的,我看到canonicalize()不是一個函數,我以爲我讀過它是一個函數。可能不會。謝謝,至於變量,也謝謝你。我編輯它。謝謝您的幫助! – mcbeav 2010-12-15 19:44:23

回答

3

到目前爲止看起來不錯。我有三個建議,雖然:

  1. 選擇較長的鹽
  2. 不要儲存鹽和密碼摘要分別
  3. 如果你的數據庫連接是不localhost使用不同的數據庫連接器:PDO不(還)支持SSL連接

編輯:此外,驗證輸入一個客戶端提供一個「用戶名」。但是因爲你的代碼示例只是一個摘錄,我想你是這麼做的。

編輯#2:當我說,「不要分別存儲鹽和密碼」我的意思是將鹽納入存儲的密碼哈希。由於您選擇的哈希算法會生成一個由[0-9a-f]組成的相當長的字符串(64個字符),因此您可能需要考慮生成一個隨機長度的鹽(在此稱爲ircmaxell),並將其連接到開頭或密碼哈希結束。所以,你最終會存儲可變長度值(96 - 128個字符)的價值無意義的(外人):

$hash = hash('sha256', $pass); 
$salt = substr(hash('sha256', mt_rand(0, 1337)), mt_rand(0, 31), 32); 
$hash = $salt . hash('sha256', $salt . $hash . $pass); 
+0

謝謝!我非常感謝評論。我不知道PDO不支持SSL。對此有何建議?再次感謝您的幫助。 – mcbeav 2010-12-15 19:13:53

+0

另外,我擔心表演。我在這裏不太瞭解,但是我可能使用了太多變量? – mcbeav 2010-12-15 19:14:34

+1

至於準備語句支持(這是一個*好東西*),我使用PHP的mysqli擴展。至於變量:我個人是可讀代碼的粉絲。所以不行。最好能夠讀取*代碼而不是解密它,因爲你第10次訪問'$ _POST ['foo'] [0]'。 – 2010-12-15 19:15:04

2

一個建議。添加密碼進入鹽醃哈希奔跑:

$hash = hash('sha256', $pass); 
$salt = createSalt(); 
$hash = hash('sha256', $salt . $hash . $pass); 

的原因是爲了避免衝突(儘管SHA-256這幾乎不可能)。比方說有,當通過sha256 ... $hash從第一輪運行是等同採用密碼bar碰撞的字符串foo,所以第二輪的哈希也會產生相同的結果:

$hash = hash('sha256', 'foo'); // "test" for example 
$hash = hash('sha256', 'bar'); // "test" since it's a collision 

$newHash = hash('sha256', $salt . $hash); //The same for both foo and bar! 

而如果你在第二輪重新引入密碼,它不會直接碰撞,因爲每個哈希輪的字符串是不同的...

編輯:至於鹽度,我會推薦這樣的東西(跨平臺):

function createSalt() { 
    $length = mt_rand(64, 128); 
    $salt = ''; 
    for ($i = 0; $i < $length; $i++) { 
     $salt .= chr(mt_rand(33, 255)); 
    } 
    return $salt; 
} 

它使用超出正常範圍(更高)的字符,但排除可能被數據庫刪除(或刪除)的公共控制字符和空白字符。請注意,它返回一個有效的ISO-8859-1(Latin-1)字符串。這不是有效的UTF-8字符串。因此,確保列的字符集是好的,或者在最糟糕的情況下在上面的代碼中更改255127(但這大大降低了鹽的強度)...

+0

感謝一噸鹽的功能。我非常感謝所有的幫助。建議也很棒!感謝所有的幫助。 – mcbeav 2010-12-15 19:37:19

+2

也爲你+1。雖然碰撞的可能性依然存在,但在您從前沒有旅行過的道路上繞過的彩票中兌現時,被雷擊擊中的可能性更高。 – 2010-12-15 19:47:49

+1

@goreSplatter:當然,假設我們正在討論'sha-256'的實現細節。如果它是'md5',那麼它就像在你有生之年遭遇閃電一樣...... – ircmaxell 2010-12-15 19:50:10