2013-08-22 116 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!'; 
     } 
    }