2013-08-22 115 views
0

我做了一個登錄腳本爲我的網站,需要一些反饋,無論是安全與否和如何改善它+幾個問題:安全登錄腳本

<?php 
    session_start(); 

    $user = "root"; 
    $host = "localhost"; 
    $pass = ""; 
    $db = "test_db"; 

    $cxn = mysqli_connect($host, $user, $pass, $db) or die ("Couldn't connect to the server. Please try again."); 

    if(isset($_POST['submit'])) { 
    $username = mysql_real_escape_string(strip_tags(trim($_POST['username']))); 
    $password = mysql_real_escape_string(strip_tags(trim($_POST['password']))); 
    $message = ""; 

    $stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?'); 
    $stmt->bind_param('s', $username); 

    $stmt->execute(); 

    $result = $stmt->get_result(); 
    while ($row = $result->fetch_assoc()) { 
     if(password_verify($password, $row['password'])) { 
     $_SESSION['username'] = $username; 
     $_SESSION['password'] = $password; 
     header("location:index.php"); 
     } else { 
     $message = "The username or password is incorrect."; 
     } 
    } 
} 

>

?另外,我只是學習有關會議,並有幾個問題:

  1. 在用戶登錄成功後,我需要他們的用戶名,以顯示任何其他頁面上。我如何使會話安全?
  2. 如何使「註銷」功能結束會話?
+2

這已經不安全了。您沒有使用參數化查詢。 –

+0

有幾個問題屬於這裏,但詢問如何改進這個屬於[代碼評論](http://codereview.stackexchange.com/)IMO –

+0

@BrendanLong,爲什麼是1而不是's'? –

回答

2
  1. 會話是服務器片面的,所以你不必擔心在這個意義上的安全性。您只需在開頭添加session_start(),然後在您想要的時候輸入echo $_SESSION['username'];

  2. 要結束會話,請使用session_destroy()


就像我在我的評論說,你需要使用參數化查詢實際上是「安全的」創建您的登錄腳本時。

+0

你能解釋你參數化查詢的含義嗎? –

+1

請參閱[此SO帖子](http://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php?rq=1) –

+0

我已更新我的PHP在原始帖子。我改變了什麼? –

1
$password = mysql_real_escape_string(strip_tags(trim($_POST['password']))); 

你爲什麼從密碼刪除空白和標籤?如果我的隨機生成的密碼是<correct horse battery stapl/>e會怎麼樣?您的代碼會將該密碼轉換爲e。這是所有你應該用密碼做:

$password = mysql_real_escape_string($_POST['password']); 

編輯:其實,你並不需要在所有逃離密碼,因爲你不能在查詢中使用它。


關於您的編輯:

$username = mysql_real_escape_string(strip_tags(trim($_POST['username']))); 
$password = mysql_real_escape_string(strip_tags(trim($_POST['password']))); 
$message = ""; 

$stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?'); 
$stmt->bind_param('s', $username); 

利用參數化查詢,你不需要逃避任何東西(這是很大的優勢),所以這應該是:

$username = trim($_POST['username']); 
$password = $_POST['password']; 
$message = ""; 

$stmt = $cxn->prepare('SELECT * FROM users WHERE username = ?'); 
$stmt->bind_param('s', $username); 

對於一些好消息,這可能是第一個堆棧溢出PHP問題,我已經看到有人正確地處理密碼:

if(password_verify($password, $row['password'])) { 

是的。

+0

謝謝。良好的捕獲,不知道爲什麼我這樣做。 –

+0

當我嘗試將1放入bind_param時,出現以下錯誤: **警告**:mysqli_stmt :: bind_param():C:\ xampp \ htdocs \ vidnut \ login中未定義的字段類型1(參數2)。 php on line 17 **致命錯誤**:調用第22行的C:\ xampp \ htdocs \ vidnut \ login.php中的非對象的成員函數fetch_assoc() –

+0

他錯了。這是爲了PDO,而不是MYSQLi。使用's',而不是1 –

3

首先,你不應該在密碼字段上應用mysql_real_escape_string。在與存儲在數據庫中的哈希進行比較之前,您正在轉義字符串,因此如果密碼包含特殊字符(如「或」),它們將會被轉義,這將改變輸出哈希值,並且登錄不起作用

例如,密碼pass123'將變爲pass123 \',它將具有不同的散列值。其次,如果您不在會話中存儲明文密碼,則會更好,因爲這意味着它們會被寫入(默認情況下未加密)到磁盤上的文件中;即使他們只是服務器端,如果服務器會受到威脅,所有登錄帳戶也會受到影響。如果在登錄後,您只需使用登錄的用戶信息設置用戶標識或數組/對象,則會更好。如果存在,您知道用戶已登錄並註銷,您只需從會話中取消設置該變量即可。

第三,爲了完全實現MySQLi無風險,您應該使用預準備語句和PDO。例如:

<?php 
    $dbh = new PDO("mysql:host=$host;dbname=$db;charset=utf8'", $user, $pass); 

    if(isset($_POST['submit'])) { 
     // Prepare the statement 
     $stmt = $dbh->prepare("SELECT * FROM users WHERE username=:username"); 

     // Bind the parameters 
     $stmt->bindParam(':username', $_POST['username'], PDO::PARAM_STR); 

     // Execute the statement 
     $stmt->execute(); 

     // Get the result 
     $user= $stmt->fetch(PDO::FETCH_OBJ); 

     if (empty($user) || !password_verify($_POST['password'], $user->password)) { 
      $message = 'Login Failed'; 

     // Login is ok, store the user in the session 
     } else { 
      $_SESSION['loggedInUser'] = $user; 

      $message = 'You are now logged in!'; 
     } 
    }