基于PHP、MySQL的登录系统存在问题
重要 按照下面答案中的建议进行操作后,客户端可以毫无问题地登录,但没有尝试实际浏览受保护的页面。当他稍后尝试这样做时,返回到与以前一样的登录,并出现“请登录”错误。经过一番努力之后,我想到了一件非常简单的事情——客户端正在使用登录脚本访问站点,并且登录脚本中的所有内容都被重定向到,因此它正在寻找的会话cookie被设置为另一个域。这也解释了为什么他在第一次登录时遇到问题,但在随后的几次登录中没有出现问题——脚本将他重定向到登录表单,而没有www 一个快速修复方法是编写一个.htaccess文件来删除www,问题解决了。当然,这也可以在登录脚本中处理,我将改进它以备将来使用 原创帖子 我使用自制CMS和登录系统开发基于PHP和MySQL的网站。我的CMS对每一个客户都是独一无二的,而且它一直都很讨人喜欢——不幸的是,我的登录系统并非如此。下面是一篇很长的帖子,但我需要讲述细节,以尝试找到解决方案。请容忍我 这个系统是相当直接的,如果不是有点大的话。每个用户都有一个SALT散列存储在MySQL表中,与SALT一起。当用户登录时,将检索他们的SALT,提交的密码将成为SALT散列 如果提交的salt散列与表中存储的散列匹配,则对用户进行身份验证。诸如名称、最后一个IP地址和帐户级别(大多数站点上为3级)等详细信息存储在分配给会话变量的数组中。然后,他们被重定向到他们登录的受限站点的登录页(仅限会员或管理员/CMS) 安全页面包括一个较小的auth.php文件,用于检查包含其详细信息的会话变量是否存在。如果没有,则会将它们重定向到该站点的登录表单,并显示一条错误消息“请登录”。如果存在,则允许它们继续,并将存储在数组中的详细信息分配给变量 许多用户报告的问题是,他们通常需要多次登录,以避免返回到带有“请登录”错误消息的登录表单,或者他们导航到安全站点中的另一个页面,并随机返回到带有相同错误的登录。因此,会话变量似乎没有设置,或者在正常使用站点期间由于某种原因被清除 第一个问题从来没有发生在我身上——通过大量的设备和网络——我在一个客户的办公室里用他们的笔记本电脑看到过。我让他们连接到我的移动热点,没有任何变化。然而,他们能够使用我的笔记本电脑和热点连接登录,没有任何问题。不幸的是,我无法使用笔记本电脑连接到他们的网络,所以不能排除这个变量 *注意-*一开始我忘了提到,在问题客户使用正确的凭据登录两到三次之后,系统会正常工作。在浏览器保持打开状态的情况下,随后的登录尝试往往会在此后顺利执行。此外,登录页面会销毁会话 以下是每个阶段的代码,从登录脚本开始: login.php基于PHP、MySQL的登录系统存在问题,php,security,login-script,Php,Security,Login Script,重要 按照下面答案中的建议进行操作后,客户端可以毫无问题地登录,但没有尝试实际浏览受保护的页面。当他稍后尝试这样做时,返回到与以前一样的登录,并出现“请登录”错误。经过一番努力之后,我想到了一件非常简单的事情——客户端正在使用登录脚本访问站点,并且登录脚本中的所有内容都被重定向到,因此它正在寻找的会话cookie被设置为另一个域。这也解释了为什么他在第一次登录时遇到问题,但在随后的几次登录中没有出现问题——脚本将他重定向到登录表单,而没有www 一个快速修复方法是编写一个.htaccess文件来
<?php
putenv("TZ=US/Eastern");
if (array_key_exists('site', $_POST)) {
$authenticate = new loginUser($_POST['username'], $_POST['password'], $_POST['site'], $_SERVER['REMOTE_ADDR']);
}
//Authenticate and log-in
class loginUser {
private $memDB, $username, $password, $site, $ip_address;
//Clean input variables
private function clean($str) {
$str = @trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return $str;
}
//Construct variables
function __construct($username, $password, $site, $ip_address) {
session_start();
$this->memDB = new PDO('mysql:host=localhost;dbname=exampleDB', 'exampleUser', 'examplePassword');
$this->username = $this->clean($username);
$this->password = $this->clean($password);
$this->site = $site;
$this->ip_address = $ip_address;
$this->authUser();
}
//Validate username
private function validateUsername($username) {
$checkUsername = $this->memDB->prepare("SELECT COUNT(*) FROM accounts WHERE username = ?");
$checkUsername->execute(array($username));
return $checkUsername->fetchColumn();
}
//Obtain and set account details
private function accountDetails() {
$fetchAccountDetails = $this->memDB->prepare("SELECT id, name_f, name_l, ipAddr, lastLogin, accountLevel, isActive
FROM accounts WHERE username = ?");
$fetchAccountDetails->execute(array($this->username));
$accountDetails = $fetchAccountDetails->fetch();
$this->updateLogin();
return $accountDetails;
}
//Update last login details
private function updateLogin() {
$updateLogin = $this->memDB->prepare("UPDATE accounts SET ipAddr = ?, lastLogin = DATE_ADD(NOW(), INTERVAL 1 HOUR) WHERE username = ?");
$updateLogin->execute(array($this->ip_address, $this->username));
}
public function authUser() {
$loginErr = array(); //Array for holding login error message
$loginErrFlag = false; //Boolean for error
//Validate submitted $_POST elements
if (!$this->username) {
$loginErr[] = "Username missing";
$loginErrFlag = true;
}
if (!$this->password) {
$loginErr[] = "Password missing";
$loginErrFlag = true;
}
if ($this->username && $this->validateUsername($this->username) == 0) {
$loginErr[] = "Username invalid";
$loginErrFlag = true;
}
if (!$loginErrFlag) {
//Fetch the password and SALT to compare to entered password
$validatePW = $this->memDB->prepare("SELECT password, salt FROM accounts WHERE username = ? LIMIT 1");
$validatePW->execute(array($this->username));
$passwordResult = $validatePW->fetch();
$dbPW = $passwordResult['password'];
$dbSalt = $passwordResult['salt'];
//Compare entered password to SALT + hash
$hashPW = hash('sha512', $dbSalt . $this->password);
if ($hashPW === $dbPW) {
//Logged in
$_SESSION['CVFD-USER-DETAILS'] = $this->accountDetails();
//Redirect to secure landing page for log-in origin (Members or Admin)
//Adding SID is a recent attempt to handle log-in problems
header("Location: http://example.com/$this->site/$this->site-main.php?" . SID);
//session_write_close() was here but was removed
exit();
} else {
//Password invalid
$loginErr[] = "Please check your password and try again";
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
//Redirect to the log-in for the origin
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
} else {
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
}
}
?>
<?php
session_start();
if (!isset($_SESSION['CVFD-USER-DETAILS']) || $_SESSION['CVFD-USER-DETAILS'] == '') {
//Not logged in
$_SESSION['CVFD_LOGIN_ERR'] = array('Please login');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$userDetails = $_SESSION['CVFD-USER-DETAILS']; //Assign user details array to variable
//Check to see if account is active
$accountStatus = $userDetails['isActive'];
$accountLevel = $userDetails['accountLevel'];
if ($accountStatus == 0) {
//Account is not yet active (pending Admin activation)
$_SESSION['CVFD_LOGIN_ERR'] = array('Your account is suspended or pending activation');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$CVFDFirstName = $userDetails['name_f'];
$CVFDLastName = $userDetails['name_l'];
$CVFDLastLogin = date("m/d/Y H:i:s", strtotime($userDetails['lastLogin']));
$CVFDAccountLevel = $userDetails['accountLevel'];
$CVFDIPAddr = $userDetails['ipAddr'];
}
}
?>
auth.php
<?php
putenv("TZ=US/Eastern");
if (array_key_exists('site', $_POST)) {
$authenticate = new loginUser($_POST['username'], $_POST['password'], $_POST['site'], $_SERVER['REMOTE_ADDR']);
}
//Authenticate and log-in
class loginUser {
private $memDB, $username, $password, $site, $ip_address;
//Clean input variables
private function clean($str) {
$str = @trim($str);
if(get_magic_quotes_gpc()) {
$str = stripslashes($str);
}
return $str;
}
//Construct variables
function __construct($username, $password, $site, $ip_address) {
session_start();
$this->memDB = new PDO('mysql:host=localhost;dbname=exampleDB', 'exampleUser', 'examplePassword');
$this->username = $this->clean($username);
$this->password = $this->clean($password);
$this->site = $site;
$this->ip_address = $ip_address;
$this->authUser();
}
//Validate username
private function validateUsername($username) {
$checkUsername = $this->memDB->prepare("SELECT COUNT(*) FROM accounts WHERE username = ?");
$checkUsername->execute(array($username));
return $checkUsername->fetchColumn();
}
//Obtain and set account details
private function accountDetails() {
$fetchAccountDetails = $this->memDB->prepare("SELECT id, name_f, name_l, ipAddr, lastLogin, accountLevel, isActive
FROM accounts WHERE username = ?");
$fetchAccountDetails->execute(array($this->username));
$accountDetails = $fetchAccountDetails->fetch();
$this->updateLogin();
return $accountDetails;
}
//Update last login details
private function updateLogin() {
$updateLogin = $this->memDB->prepare("UPDATE accounts SET ipAddr = ?, lastLogin = DATE_ADD(NOW(), INTERVAL 1 HOUR) WHERE username = ?");
$updateLogin->execute(array($this->ip_address, $this->username));
}
public function authUser() {
$loginErr = array(); //Array for holding login error message
$loginErrFlag = false; //Boolean for error
//Validate submitted $_POST elements
if (!$this->username) {
$loginErr[] = "Username missing";
$loginErrFlag = true;
}
if (!$this->password) {
$loginErr[] = "Password missing";
$loginErrFlag = true;
}
if ($this->username && $this->validateUsername($this->username) == 0) {
$loginErr[] = "Username invalid";
$loginErrFlag = true;
}
if (!$loginErrFlag) {
//Fetch the password and SALT to compare to entered password
$validatePW = $this->memDB->prepare("SELECT password, salt FROM accounts WHERE username = ? LIMIT 1");
$validatePW->execute(array($this->username));
$passwordResult = $validatePW->fetch();
$dbPW = $passwordResult['password'];
$dbSalt = $passwordResult['salt'];
//Compare entered password to SALT + hash
$hashPW = hash('sha512', $dbSalt . $this->password);
if ($hashPW === $dbPW) {
//Logged in
$_SESSION['CVFD-USER-DETAILS'] = $this->accountDetails();
//Redirect to secure landing page for log-in origin (Members or Admin)
//Adding SID is a recent attempt to handle log-in problems
header("Location: http://example.com/$this->site/$this->site-main.php?" . SID);
//session_write_close() was here but was removed
exit();
} else {
//Password invalid
$loginErr[] = "Please check your password and try again";
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
//Redirect to the log-in for the origin
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
} else {
$_SESSION['CVFD_LOGIN_ERR'] = $loginErr;
header("Location: http://example.com/$this->site");
session_write_close();
exit();
}
}
}
?>
<?php
session_start();
if (!isset($_SESSION['CVFD-USER-DETAILS']) || $_SESSION['CVFD-USER-DETAILS'] == '') {
//Not logged in
$_SESSION['CVFD_LOGIN_ERR'] = array('Please login');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$userDetails = $_SESSION['CVFD-USER-DETAILS']; //Assign user details array to variable
//Check to see if account is active
$accountStatus = $userDetails['isActive'];
$accountLevel = $userDetails['accountLevel'];
if ($accountStatus == 0) {
//Account is not yet active (pending Admin activation)
$_SESSION['CVFD_LOGIN_ERR'] = array('Your account is suspended or pending activation');
header('Location: http://example.com/members');
session_write_close();
exit();
} else {
$CVFDFirstName = $userDetails['name_f'];
$CVFDLastName = $userDetails['name_l'];
$CVFDLastLogin = date("m/d/Y H:i:s", strtotime($userDetails['lastLogin']));
$CVFDAccountLevel = $userDetails['accountLevel'];
$CVFDIPAddr = $userDetails['ipAddr'];
}
}
?>
下面是auth.php如何包含在安全文件中-
<?php
if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) ob_start("ob_gzhandler"); else ob_start();
require($_SERVER['DOCUMENT_ROOT'] . '/members/includes/handlers/handler.auth.php');
我突然想到的一件事是:
header('Location: http://example.com/members');
session_write_close();
exit();
我会将会话\u write\u close()
调用放在标题('location…')之前。
日志中是否显示任何“已发送邮件头”错误
我想到的另一件事是一些AJAX比赛条件。登录页面是否有异步调用?我突然想到的一件事是:
header('Location: http://example.com/members');
session_write_close();
exit();
我会将会话\u write\u close()
调用放在标题('location…')之前。
日志中是否显示任何“已发送邮件头”错误
我想到的另一件事是一些AJAX比赛条件。登录页面是否有异步调用?我登录系统的方式是只使用会话id,而不是在会话本身中存储任何内容。当一个用户登录他们的散列用户代理数据、他们的会话ID、他们的用户ID(对应于一个用户表)和一个到期时间被放入一个表(通常称为“active_users”)中时,我会在启动会话的每个管理限制页面中包含一个登录头文件,检索用户会话ID并检查该会话ID是否在active users表中,以及被检查的用户是否具有相同的用户代理数据,则不会超过过期时间。如果该查询没有返回任何内容,则它们不会登录并被跳出
这就是我制作的大多数登录系统的工作方式,我没有遇到任何问题。我登录系统的方式是只使用会话id,而不是在会话本身中存储任何内容。当一个用户登录他们的散列用户代理数据、他们的会话ID、他们的用户ID(对应于一个用户表)和一个到期时间被放入一个表(通常称为“active_users”)中时,我会在启动会话的每个管理限制页面中包含一个登录头文件,检索用户会话ID并检查该会话ID是否在active users表中,以及被检查的用户是否具有相同的用户代理数据,则不会超过过期时间。如果该查询没有返回任何内容,则它们不会登录并被跳出
这就是我制作的大多数登录系统的工作原理,我没有遇到任何问题。成功!仍然需要缩小导致问题g的确切变化范围