我正在建立一个带有用户页面的网站。目前,它是通过以下方式设置的:
用户在相应的字段中输入用户名和密码,然后按提交。
js代码使用纯文本的密码和用户名将get请求发送到php脚本。
如果凭据正确,则php将随机生成的会话令牌返回到js代码,否则返回错误消息。
现在,用户在登录时进行的所有将来交互都将要求在任何get请求中提供会话令牌。然后,PHP将检查会话令牌是否正确。
在后端,用户名与盐和盐腌密码的哈希(sha256)一起存储在数据库中。 (盐是在创建帐户时随机生成的。)
我的问题如下:上面的描述中是否有不安全的地方?如果是这样,应该怎么做。更广泛地讲,围绕网站设置用户登录页面或帐户系统的最佳实践是什么。谢谢
答案 0 :(得分:1)
您为什么要重新发明轮子。
Php已经具有内置的密码加密功能,所以为什么要使用 Sha256 + Salt 。
它们又是两种身份验证 1.)基于会话的登录 2.)基于令牌的登录。
在撰写本文时,您将会话登录与令牌登录结合在一起。您将需要决定要申请哪一个。
因此,您需要了解许多php验证或清除功能,以确保代码更安全。
1。)使用strip_tags()
这将从表单输入或变量中删除所有html元素 例如
$email = strip_tags($_POST['email']);
2。)使用htmlentities或htmlspecialchars防止XSS攻击。
这会将htmls标记转换为它们各自的实体。仅在将结果打印或回显到html页面时使用 您将在welcome.php页面中看到我如何使用它 查看应用程序:
$email = htmlentities($_POST['email']);
3。)转义变量以防止sql注入攻击
如果使用的是Mysqli,则最好使用准备使用的sql方法Statement。 另外,您仍然可以使用 mysqli_real_escape_string()函数对变量进行转义 查看应用程序
// escape variables Against sql injections
$email = mysqli_real_escape_string($conn, $_POST['email']);
4。)如果使用基于会话的登录,则需要使用 sessionId重新生成方法。这将有助于重新生成新会话 标识为用户登录名,从而防止会话固定攻击。不用担心,您将需要在下面的login.php代码中使用它 查看应用程序:
// first you will need to initialize sessions
session_start();
session_regenerate_id();
这只是其他安全措施中的少数
让我们看看使用php密码验证功能的基于会话的登录
假设这是您的
registeration.php
<?php
$conn = mysqli_connect("localhost","root","","demo");
if(!$conn){
die("Connection error: " . mysqli_connect_error());
}
if(isset($_POST['submit'])){
$firstName = mysqli_real_escape_string($conn,$_POST['first_name']);
$surName = mysqli_real_escape_string($conn,$_POST['surname']);
$email = mysqli_real_escape_string($conn,$_POST['email']);
$password = mysqli_real_escape_string($conn,$_POST['password']);
$options = array("cost"=>4);
$hashPassword = password_hash($password,PASSWORD_BCRYPT,$options);
$sql = "insert into users (first_name, last_name,email, password) value('".$firstName."', '".$surName."', '".$email."','".$hashPassword."')";
$result = mysqli_query($conn, $sql);
if($result)
{
echo "Registration successfully";
}
}
?>
这就是您的 login.php 代码的样子
<?php
$conn = mysqli_connect("localhost","root","","demo");
if(!$conn){
die("Connection error: " . mysqli_connect_error());
}
if(isset($_POST['submit'])){
$email = mysqli_real_escape_string($conn,$_POST['email']);
$password = mysqli_real_escape_string($conn,$_POST['password']);
$sql = "select * from users where email = '".$email."'";
$rs = mysqli_query($conn,$sql);
$numRows = mysqli_num_rows($rs);
if($numRows == 1){
$row = mysqli_fetch_assoc($rs);
if(password_verify($password,$row['password'])){
echo "Password verified and ok";
// initialize session if things where ok.
session_start();
session_regenerate_id();
$_SESSION['surname'] = $row['surname'];
$_SESSION['first_name'] = $row['first_name'];
$_SESSION['email'] = $row['email'];
// take me to welcome.php page
header('Location: welcome.php');
}
else{
echo "Wrong Password details";
}
}
else{
echo "User does not exist";
}
}
?>
Welcome.php 现在看起来像下面的代码,以显示经过身份验证的用户的会话信息。
//使用htmlentities或htmlspecialchars防止XSS攻击。
<?php echo htmlentities($_SESSION['surname'], ENT_QUOTES, "UTF-8"); ?>
<?php echo htmlspecialchars($_SESSION['first_name'], ENT_QUOTES, "UTF-8"); ?>
<?php echo htmlentities($_SESSION['email'], ENT_QUOTES, "UTF-8"); ?>
现在,在我看到的帖子中,您看到在哪里写了每个http请求发送一个生成的令牌的信息。在这种情况下,我想你是 尝试减轻 CSRF攻击。
登录后,这是最好,最安全的操作方式
为防止CSRF,您需要验证一次性令牌,该令牌已过POST,并与当前会话相关联。 类似以下内容。 。
在用户请求例如插入付款记录的页面上:
payment.php
<?php
session_start();
$token= md5(uniqid());
$_SESSION['payment_token']= $token;
session_write_close();
?>
<html>
<body>
<form method="post" action="payment_save.php">
<input type="hidden" name="token" value="<?php echo $token; ?>" />
Amount: <input type="hidden" name="token" value="100 usd" />
<input type="submit" value="submit" />
</form>
</body>
</html>
然后涉及实际插入记录:
payment_save.php
<?php
session_start();
$token = $_SESSION['payment_token'];
unset($_SESSION['payment_token']);
session_write_close();
if ($token && $_POST['token']==$token) {
// Insert the record for payment
} else {
// log message. You are vulnerable to CSRF attack.
}
?>
令牌应该很难猜测,对于每个插入请求都是唯一的,只能通过$ _POST接受,并且在几分钟后过期 (本图中未显示有效期。)