PHP安全-會話數據暴露(二)
當你關注于防止源碼的暴露時,你的會話數據只同樣存在著風險。在默認情況下,SESSION保存在/tmp目錄下。這樣做在很多情形下是很方便的,其中之一是所有用戶都有對/tmp的寫入權限,這樣Apache同樣也有權限進行寫入。雖然其他用戶不能直接從shell環(huán)境讀取這些會話文件,但他們可以寫一個簡單的腳本來進行讀取:
<?php
header(’Content-Type: text/plain’);
session_start();
$path = ini_get(’session.save_path’);
$handle = dir($path);
while ($filename = $handle->read())
{
if (substr($filename, 0, 5) == ’sess_’)
{
$data = file_get_contents('$path/$filename');
if (!empty($data))
{
session_decode($data);
$session = $_SESSION;
$_SESSION = array();
echo 'Session [' . substr($filename, 5) . ']n';
print_r($session);
echo 'n--nn';
}
}
}
?>
這個腳本在session.save_path所定義的會話文件保存目錄中搜索以sess_為前綴的文件。找到文件后,即對它的內容進行解析并用print_r()函數顯示它的內容。這樣其它開發(fā)者就容易地取得了你的用戶的會話數據。
解決這個問題的最好方法是把你的會話數據存入用用戶名和密碼保護的數據庫中。由于數據庫的訪問是受控的,這樣就多了一層額外的保護。通過應用前節(jié)中提及的技巧,數據庫可以為你的敏感數據提供一個安全的存放地,同時你應該保持警惕,你的數據庫安全性正變得越來越重要。
為在數據庫中保存會話數據,首先需要建立一個數據表:
CREATE TABLE sessions
(
id varchar(32) NOT NULL,
access int(10) unsigned,
data text,
PRIMARY KEY (id)
);
如果你使用的是MySQL,則表結構描述如下:
mysql> DESCRIBE sessions;
+--------+------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------+------------------+------+-----+---------+-------+
| id | varchar(32) | | PRI | | |
| access | int(10) unsigned | YES | | NULL | |
| data | text | YES | | NULL | |
+--------+------------------+------+-----+---------+-------+
如要使會話數據能保存在此表中,你需要使用session_set_save_handler( )函數來編輯PHP的內建會話機制:
<?php
session_set_save_handler(’_open’,
’_close’,
’_read’,
’_write’,
’_destroy’,
’_clean’);
?>
Each of these six arguments is the name of a function that you must write. These functions handle the following tasks:
以上的六個參數每一個都代表著需要你編寫的函數的名稱,他們對下面的任務進行處理:
l打開會話存儲
l關閉會話存儲
l讀取會話數據
l寫入會話數據
l消滅會話數據
l清除舊會話數據
我有意使用了有意義的名稱,這樣你可以一下看出它們的目的。命名是任意的,但你可能希望用下劃線開頭(如此處所示)或其它的命名約定來防止名稱沖突。下面是這些函數(使用MySQL)的示例:
<?php
function _open()
{
global $_sess_db;
$db_user = $_SERVER[’DB_USER’];
$db_pass = $_SERVER[’DB_PASS’];
$db_host = ’localhost’;
if ($_sess_db = mysql_connect($db_host, $db_user, $db_pass))
{
return mysql_select_db(’sessions’, $_sess_db);
}
return FALSE;
}
function _close()
{
global $_sess_db;
return mysql_close($_sess_db);
}
function _read($id)
{
global $_sess_db;
$id = mysql_real_escape_string($id);
$sql = 'SELECT data
FROM sessions
WHERE id = ’$id’';
if ($result = mysql_query($sql, $_sess_db))
{
if (mysql_num_rows($result))
{
$record = mysql_fetch_assoc($result);
return $record[’data’];
}
}
return ’’;
}
function _write($id, $data)
{
global $_sess_db;
$access = time();
$id = mysql_real_escape_string($id);
$access = mysql_real_escape_string($access);
$data = mysql_real_escape_string($data);
$sql = 'REPLACE
INTO sessions
VALUES (’$id’, ’$access’, ’$data’)';
return mysql_query($sql, $_sess_db);
}
function _destroy($id)
{
global $_sess_db;
$id = mysql_real_escape_string($id);
$sql = 'DELETE
FROM sessions
WHERE id = ’$id’';
return mysql_query($sql, $_sess_db);
}
function _clean($max)
{
global $_sess_db;
$old = time() - $max;
$old = mysql_real_escape_string($old);
$sql = 'DELETE
FROM sessions
WHERE access < ’$old’';
return mysql_query($sql, $_sess_db);
}
?>
你必須要在session_start( )之前調用session_set_save_handler( )函數,但你可以在任何地方對這些函數本身進行定義。
這個流程的漂亮之處在于你無須對代碼進行編輯或變化使用會話的方式。$_SESSION依然存在,行為依舊,還是由PHP來產生與傳遞會識標識,對有關會話的配置變更同樣還會生效。所有你需要做的只是調用這一個函數(同時建立由它指定的所有函數),PHP就會照顧余下的事情。
相關文章: