php - PHP $_SESSION 和 $_COOKIE 奇怪的行为
问题描述
我正在尝试将 COOKIES 安装到我的网站中。
我在 GitHub 上找到了一个脚本:https ://github.com/jotaroita/secure-login-php7-remember-register-resetpw
我已经实现了脚本,并且能够登录。
我可以只使用 SESSION 登录,或者我可以同时使用 SESSION 登录并设置“记住我 -COOKIE”。
为了测试 COOKIE,我将 SESSION 设置为 1 分钟后过期。$expireAfter = 1;
Senario:
我登录网站并勾选“记住我”。会话开始并设置了 cookie。一切都很好!
## last action: 1 seconds ago
skip the cookie check: session already set
我等待 60 秒并重新加载页面。会话销毁并且 Cookie 读取:
## last action: 108 seconds ago
session destroy for inactivity
cookie read
cookie valid format
cookie right selector
cookie right token
set a new token in DB and in cookie
session set
<-在此消息中我可以输出会话数据:$_SESSION['user']
始终
但在我的其他页面(home.php)$_SESSION['user']
中是空的?!(我包括来自:check.php 的 SESSION 和 COOKIE 检查)if(isset($_SESSION['last_action'])){
返回 true
如果我再等 60 秒并重新加载页面,则if(isset($_SESSION['last_action'])){
返回 false。但是现在$_SESSION['user']
设置好了。
如果我再等 60 秒并重新加载页面。if(isset($_SESSION['last_action'])){
返回真。但现在$_SESSION['user']
是空的。
主页.php
<?php
//START SESSION
session_start();
//CONNECT TO DB, SET HEADER, SET TIMEZONE, CHECK FOR LOGIN-COOKIES
include("check.php");
//CHECK IF SESSION EXIST
if(!isset($_SESSION['user'])) {
$isSesstion = false;
header("Location: /index.php");
}
else{
$isSesstion = true;
}
.....
检查.php
<?php
define ("PEPPER",''); //random string for extra salt, use if you want.
define ("WEBSITE",'mydomain.com'); //your web site without http:// without final /
define ("SCRIPTFOLDER",'/login'); //direcory of the script start with a / if you installed the script in the root write just a / never finish with a /
$hosting="localhost"; //your host or localhost
$database="db"; //your database name
$database_user="user"; //your username for database
$database_password="pwd"; //your database password
require_once('pdo_db.php');
//generate random sequence or numbers and letter avoid problems with special chars
function aZ ($n=12) {
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$bytes = random_ver($n);
$result="";
foreach (str_split($bytes) as $byte) $result .= $chars[ord($byte) % strlen($chars)];
return $result;
}
//generate random and avoid compatibility problem with php version
function random_ver ($n=10) {
$v=(int)phpversion()+0;
if ($v<7) {
//if php version < 7 use the old function for generate random bytes
return openssl_random_pseudo_bytes($n);
}else{
//random_bytes is better but works only with php version > 7
return random_bytes($n);
}
}
// ********************************
// * SESSION TIME UPDATE *
// ********************************
//Expire the session if user is inactive for 15 minutes or more.
//if u want to check how cookie works let the session id expire (wait X minutes without action, maybe set expireAfter to low value),
//close the browser then open again
$expireAfter = 1;
//Check to see if our "last action" session
//variable has been set.
if(isset($_SESSION['last_action'])){ echo 'IN';
//Figure out how many seconds have passed
//since the user was last active.
$secondsInactive = time() - $_SESSION['last_action'];
//Convert our minutes into seconds.
$expireAfterSeconds = $expireAfter * 60;
//Check to see if they have been inactive for too long.
$debug.="last action: $secondsInactive seconds ago<br>";
if($secondsInactive >= $expireAfterSeconds){
//User has been inactive for too long.
//Kill their session.
session_unset();
session_destroy();
$debug.="session destroy for inactivity<br>";
}
}
//Assign the current timestamp as the user's
//latest activity
$_SESSION['last_action'] = time();
// *********************************
// * CHECK AUTO LOG-IN WITH COOKIE *
// *********************************
//if session is not set, but cookie exists
if (empty($_SESSION['user']) && !empty($_COOKIE['remember']) && $_GET["logout"]!=1) {
$debug.="cookie read<br>";
list($selector, $authenticator) = explode(':', urldecode($_COOKIE['remember']));
//get from database the row with id and token related to selector code in the cookie
$sql = $db->prepare("SELECT * FROM user_tokens
WHERE selector = ? limit 1");
$sql->bindParam(1, $selector);
$sql->execute();
$row = $sql->fetch(PDO::FETCH_ASSOC);
if (empty($authenticator) or empty($selector))
$debug.="cookie invalid format<br>";
//continue to check the authenticator only if the selector in the cookie is present in the database
if (($sql->rowCount() > 0) && !empty($authenticator) && !empty($selector)) {
$debug.="cookie valid format<br>";
// the token provided is like the token in the database
// the functions password_verify and password_hash add secure salt and avoid timing attacks
if (password_verify(base64_decode($authenticator), $row['hashedvalidator'])){
//SET SESSION DATA
$sql = $db->prepare("SELECT * FROM users WHERE id = ?");
$sql->bindParam(1, $row['userid']);
$sql->execute();
$session_data = $sql->fetch(PDO::FETCH_ASSOC);
//UNSET VARS
unset($session_data['password']);
$_SESSION['user'] = $session_data;
//update database with a new token for the same selector and set the cookie again
$authenticator = bin2hex(random_ver(33));
$res=$db->prepare("UPDATE user_tokens SET hashedvalidator = ? , expires = FROM_UNIXTIME(".(time() + 864000*7).") , ip = ? WHERE selector = ?");
$res->execute(array(password_hash($authenticator, PASSWORD_DEFAULT, ['cost' => 12]),$_SERVER['REMOTE_ADDR'],$selector));
//set the cookie
$setc = setcookie(
'remember',
$selector.':'.base64_encode($authenticator),
time() + 864000*7, //the cookie will be valid for 7 days, or till log-out (if u want change it, modify the login.php file too)
'/',
WEBSITE,
false, // TLS-only set to true if u have a website on https://
false // http-only
);
$debug.="cookie right selector<br>cookie right token<br>set a new token in DB and in cookie<br>session set ".$_SESSION['user']['usr_fname']."<br>";
} else {
//selector exists but token doesnt match. that could be a secure problem, all selector/authenticator in database for that user will be deleted
$res=$db->prepare("DELETE FROM user_tokens WHERE userid = ".$row["userid"]);
$res->execute();
$debug.="cookie right selector<br>cookie wrong token (all DB entry for that user are deleted)<br>";
}
} else {
$debug.="selector not found in DB<br>";
}
} else {
$debug.="skip the cookie check: ";
if (!empty($_SESSION['user'])) $debug.="session already set<br>";
if (empty($_COOKIE['remember'])) $debug.="no cookie set<br>";
}
?>
那么,代码有什么问题?
为什么每次刷新网页时 $_SESSION 都会充满数据?为什么每次刷新页面
都不返回true?以及为什么 $_SESSION 一直在调试消息中携带数据......但它没有被携带到 home.php 中?if(isset($_SESSION['last_action'])){
session set ".$_SESSION['user']['usr_fname']."
include(check.php");
你需要更多的代码吗?只是要求它!
解决方案
我想我发现了问题。
取消设置并销毁会话后。我不得不开始一个新的会话。
奇怪的是,这种情况每秒钟才发生一次。但是添加session_start();
解决了问题!
if(isset($_SESSION['last_action'])){
$secondsInactive = time() - $_SESSION['last_action'];
$expireAfterSeconds = $expireAfter * 60;
$debug.="last action: $secondsInactive seconds ago<br>";
if($secondsInactive >= $expireAfterSeconds){
//User has been inactive for too long.
//Kill their session.
session_unset();
session_destroy();
$debug.="session destroy for inactivity<br>";
}
}
...
session_start();
$_SESSION['user'] = $session_data;
...
推荐阅读
- c++ - 编译 g++ 时覆盖文件
- geolocation - 如何在给定坐标的情况下评估位置的偏远?
- xml - Powershell 检查是否将用户或角色添加到 .Net 授权规则
- javascript - 如何在 NuxtJS 上正确显示传单地图
- mysql - MySQL INSERT INTO ON DUPLICATE KEY UPDATE 非连续 id
- python - 我如何按 ID 分组并相互添加列
- python - 按方法值过滤 - SQL 变量过多错误
- arrays - 使用 JSON 文件,我将如何使用 JQ 列出数组内的两个对象?
- docker - 使用 nvidia gpu 创建 docker-compose 时“不允许使用‘设备’属性”
- python - 将唯一组合放入文本文件并检测现有数据