[英]PHP Sessions and session_regenerate_id
我已經嘗試解決這個問題很多周了。
我已經建立了一個類來保護PHP會話,並且可以正常工作,除非有人嘗試執行注冊(問題2),並且如果某些功能被禁用(導致問題2發生),其余的網站工作正常。
所以這是問題所在:
我知道問題可能在哪里,但我不知道如何解決。
我有一個私有的靜態函數 ,在調用session_start()之后直接調用它
private static function GenerateSessionData()
{
$_SESSION['loggedin'] = '';
$_SESSION['username'] = '';
$_SESSION['remember_me'] = '';
$_SESSION['preferredlanguage'] = '';
$_SESSION['generated_captcha'] = '';
}
這樣做是為了預定義會話要使用的變量(我90%確信這就是會話之后變為空白的原因)。
我不確定的是為什么。
這是完整的會話類:
<?php
Class Session
{
public static $DBConnection;
private static $SessionCreated = false;
public function __construct($Database)
{
session_set_save_handler(array($this, 'Open'), array($this, 'Close'), array($this, 'Read'), array($this, 'Write'), array($this, 'Destroy'), array($this, 'GarbageCollector'));
register_shutdown_function('session_write_close');
Session::$DBConnection = $Database::$Connection;
}
private static function GenerateSessionData()
{
$_SESSION['loggedin'] = '';
$_SESSION['username'] = '';
$_SESSION['remember_me'] = '';
$_SESSION['preferredlanguage'] = '';
$_SESSION['generated_captcha'] = '';
}
public static function UpdateSession($Data)
{
if(!isset($_SESSION['loggedin']))
Session::GenerateSessionData();
foreach($Data as $key=>$value)
$_SESSION[$key] = $value;
}
public static function GenerateCSRFToken()
{
$InitialString = "abcdefghijklmnopqrstuvwxyz1234567890";
$PartOne = substr(str_shuffle($InitialString),0,8);
$PartTwo = substr(str_shuffle($InitialString),0,4);
$PartThree = substr(str_shuffle($InitialString),0,4);
$PartFour = substr(str_shuffle($InitialString),0,4);
$PartFive = substr(str_shuffle($InitialString),0,12);
$FinalCode = $PartOne.'-'.$PartTwo.'-'.$PartThree.'-'.$PartFour.'-'.$PartFive;
$_SESSION['generated_csrf'] = $FinalCode;
return $FinalCode;
}
public static function ValidateCSRFToken($Token)
{
if(isset($Token) && $Token == $_SESSION['generated_csrf'])
{
unset($_SESSION['generated_csrf']);
return true;
}
else
return false;
}
public static function UnsetKeys($Keys)
{
foreach($Keys as $Key)
unset($_SESSION[$Key]);
}
public static function Start($SessionName, $Secure)
{
$HTTPOnly = true;
$Session_Hash = 'sha512';
if(in_array($Session_Hash, hash_algos()))
ini_set('session.hash_function', $Session_Hash);
ini_set('session.hash_bits_per_character', 6);
ini_set('session.use_only_cookies', 1);
$CookieParameters = session_get_cookie_params();
session_set_cookie_params($CookieParameters["lifetime"], $CookieParameters["path"], $CookieParameters["domain"], $Secure, $HTTPOnly);
session_name($SessionName);
session_start();
session_regenerate_id(true);
if(!Session::$SessionCreated)
if(!isset($_SESSION['loggedin']))
Session::GenerateSessionData();
Session::$SessionCreated = true;
}
static function Open()
{
if(is_null(Session::$DBConnection))
{
die("Unable to establish connection with database for Secure Session!");
return false;
}
else
return true;
}
static function Close()
{
Session::$DBConnection = null;
return true;
}
static function Read($SessionID)
{
$Statement = Session::$DBConnection->prepare("SELECT data FROM sessions WHERE id = :sessionid LIMIT 1");
$Statement->bindParam(':sessionid', $SessionID);
$Statement->execute();
$Result = $Statement->fetch(PDO::FETCH_ASSOC);
$Key = Session::GetKey($SessionID);
$Data = Session::Decrypt($Result['data'], $Key);
return $Data;
}
static function Write($SessionID, $SessionData)
{
$Key = Session::GetKey($SessionID);
$Data = Session::Encrypt($SessionData, $Key);
$TimeNow = time();
$Statement = Session::$DBConnection->prepare('REPLACE INTO sessions (id, set_time, data, session_key) VALUES (:sessionid, :creation_time, :session_data, :session_key)');
$Statement->bindParam(':sessionid', $SessionID);
$Statement->bindParam(':creation_time', $TimeNow);
$Statement->bindParam(':session_data', $Data);
$Statement->bindParam(':session_key', $Key);
$Statement->execute();
return true;
}
static function Destroy($SessionID)
{
$Statement = Session::$DBConnection->prepare('DELETE FROM sessions WHERE id = :sessionid');
$Statement->bindParam(':sessionid', $SessionID);
$Statement->execute();
Session::$SessionCreated = false;
return true;
}
private static function GarbageCollector($Max)
{
$Statement = Session::$DBConnection->prepare('DELETE FROM sessions WHERE set_time < :maxtime');
$OldSessions = time()-$Max;
$Statement->bindParam(':maxtime', $OldSessions);
$Statement->execute();
return true;
}
private static function GetKey($SessionID)
{
$Statement = Session::$DBConnection->prepare('SELECT session_key FROM sessions WHERE id = :sessionid LIMIT 1');
$Statement->bindParam(':sessionid', $SessionID);
$Statement->execute();
$Result = $Statement->fetch(PDO::FETCH_ASSOC);
if($Result['session_key'] != '')
return $Result['session_key'];
else
return hash('sha512', uniqid(mt_rand(1, mt_getrandmax()), true));
}
private static function Encrypt($SessionData, $SessionKey)
{
$Salt = "06wirrdzHDvc*t*nJn9VWIfET+|co*pm~CbtT5P*S2IPD-VmEfd+CX2wrvZ";
$SessionKey = substr(hash('sha256', $Salt.$SessionKey.$Salt), 0, 32);
$Get_IV_Size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$IV = mcrypt_create_iv($Get_IV_Size, MCRYPT_RAND);
$Encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $SessionKey, $SessionData, MCRYPT_MODE_ECB, $IV));
return $Encrypted;
}
private static function Decrypt($SessionData, $SessionKey)
{
$Salt = "06wirrdzHDvc*t*nJn9VWIfET+|co*pm~CbtT5P*S2IPD-VmEfd+CX2wrvZ";
$SessionKey = substr(hash('sha256', $Salt.$SessionKey.$Salt), 0, 32);
$Get_IV_Size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
$IV = mcrypt_create_iv($Get_IV_Size, MCRYPT_RAND);
$Decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $SessionKey, base64_decode($SessionData), MCRYPT_MODE_ECB, $IV);
return $Decrypted;
}
}
?>
而且我不能排除該私有靜態函數 (提到的第一個函數 ),因為那樣我將無法設置變量。
您可能會說:“但是有一個UpdateSession方法”。
是的...有點...但是問題是,由於我猜不到我的知識,我把腳本弄亂了,邏輯錯了。
以下是鏈接(以簡化理解):
Sessions.FreedomCore.php-會話類
String.FreedomCore.php-驗證碼生成(指向確切的行)
pager.php-帳戶創建過程(僅適用於session_regenerate_id)
pager.php-驗證碼顯示過程(在某些情況下始終有效)
pager.php-執行登錄案例(由於某種原因無論如何都沒有問題)
如果您對它的實際工作方式超級感興趣(我的意思是4次刷新后它如何取消對用戶的授權)
請到這里
用戶名: test
密碼: 123456
因此,問題是:如何修改我的類,如何使用當前方法使用session_regenerate_id(true)保存會話數據,並防止在調用session_regenerate_id之后刷新它。
這些鏈接直接指向腳本中有問題的區域。
任何幫助都非常感謝。
非常感謝您的幫助!
您正在體驗所謂的Cookie競賽條件 。
當您使用session_regenerate_id(true)
PHP將創建一個新的會話ID(包含舊會話的數據),並針對每個請求從數據庫中刪除該舊會話。
現在,您的網站包含許多需要加載的元素,例如/pager.php
或/data/menu.json
。 每次給瀏覽器分配一個新的會話ID 。 通常這不是問題,但是現代瀏覽器會並行執行請求:
session_id = a
請求pager.php
session_id = a
請求data/menu.json
pager.php
刪除sessions_id = a
session_id = b
並向我的瀏覽器返回session_id = b
。 data/menu.json
在數據庫中找不到session_id = a
並假設我是新訪客,並給我session_id = c
現在,它取決於瀏覽器以哪個順序接收和解析哪個請求。
案例A data/menu.json
被解析:瀏覽器存儲session_id = c
。 然后解析pager.php
的響應,瀏覽pager.php
b覆蓋session_id。 對於任何下一個請求,它將使用session_id = b
。
案例B pager.php
解析pager.php
,然后解析data/menu.json
。 瀏覽器現在存儲session_id = c
並且您已注銷 。
這解釋了為什么它有時可以工作(例如4或6次刷新),而有時卻沒有。
結論:不要使用session_regenerate_id();
沒有很好的理由!
請提出一個新的問題,為什么驗證碼創建機制在注冊頁面上不起作用,而在登錄頁面上不起作用。
有關加密的一些注意事項。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.