Якщо register_globals=off (відключені), то користуватися session_register() для реєстрації змінних, переданих методами POST або GET, не можна, тобто це просто не працює. І взагалі, не рекомендується одночасно використовувати обидва методи реєстрації змінних $_SESSION і session_register(). (Починаючи з версією PHP 5.3.0 не рекомендується для реєстрації змінних сесії використовувати функцію session_register(); більш того, починаючи з версією PHP 6.0.0, ця функція стане неприступна. Натомість, для реєстрації змінних сесії рекомендується користуватися масивом $_SESSION.)
Приклад 12.3. Реєстрація змінних
Зареєструємо логін і пароль, що вводиться користувачем на сторінці авторизації.
<?
session_start();
// створюємо нову сесію або відновлюємо поточну
if (!isset($_GET['go'])){
echo "<form>
Login: <input type=text name=login>
Password: <input type=password name=passwd>
<input type=submit name=go value=Go>
</form>";
}else {
$_SESSION['login']=$_GET['login'];
// реєструємо змінну login
$_SESSION['passwd']=$_GET['passwd'];
// реєструємо змінну passwd
// тепер логін і пароль - глобальні змінні для цієї сесії
if ($_GET['login']=="pit" && $_GET['passwd']=="123") {
Header("Location: secret_info.php");
// перенаправляємо на сторінку secret_info.php
}else echo "Невірні дані. Спробуйте ще раз<br>";
}
print_r($_SESSION);
// виводимо всі змінні сесії
?>
Лістинг 12.3a. authorize.php
Тепер, потрапивши на сторіночку secret_info.php, та і на будь-яку іншу сторінку сайту, ми зможемо працювати з введеними користувачем логіном і паролем, які зберігатимуться в масиві $_SESSION. Таким чином, якщо змінити код секретної сторіночки (помітьте, ми перейменували її в secret_info.php) так:
<?php
session_start();
// створюємо нову сесію або відновлюємо поточну
print_r($_SESSION);
// виводимо всі змінні сесії
?>
<html>
<head><title>Secret info</title></head>
<body>
<p>Тут я хочу ділитися секретами з другом Петей.
</body>
</html>
Лістинг 12.3b. secret_info.php
То ми одержимо в браузері на секретній сторінці наступне:
Array ( [login] => pit [passwd] => 123 )
Тут я хочу ділитися секретами з другом Петей.
У результаті одержимо список змінних, зареєстрованих на authorize.php і, власне, саму секретну сторіночку.
Що це нам дає? Припустимо, хакер хоче прочитати секрети Васи і Петі. І він якось взнав, як називається секретна сторіночка (або сторіночки). Тоді він може спробувати просто ввести її адресу в рядку браузера, минувши сторінку авторизації (введення пароля). Щоб уникнути такого проникнення в наші таємниці, потрібно дописати всього пару рядків в код секретних сторіночок:
<?php
session_start();
// створюємо нову сесію або відновлюємо поточну
print_r($_SESSION);
// виводимо всі змінні сесії
if (!($_SESSION['login']=="pit" && $_SESSION['passwd']==123))
// перевіряємо правильність пароля-логіна
Header("Location: authorize.php");
// якщо помилка, то перенаправляємо на сторінку авторизації
?>
<html>
<head><title>Secret info</title></head>
// тут розташовується секретна інформація :)
</html>
Лістинг 12.3c. 2-а версія secret_info.php
Окрім уміння реєструвати змінні сесії (тобто робити їх глобальними протягом всього сеансу роботи), корисно також уміти видаляти такі змінні і сесію в цілому.
Функція session_unregister(ім’я_змінної) видаляє глобальну змінну з поточної сесії (тобто видаляє її із списку зареєстрованих змінних). Якщо реєстрація проводилася з допомогою $_SESSION ( $HTTP_SESSION_VARS для версії PHP 4.0.6 і більш ранніх), то використовують мовну конструкцію unset(). Вона не повертає ніякого значення, а просто знищує вказані змінні.
Де це може стати в нагоді? Наприклад, для знищення даних про відвідувача (зокрема, логіна і пароля) після його відходу з секретної сторіночки. Якщо правильні логін і пароль збережуться і вікно браузера після відвідин сайту не закрили, то будь-який інший користувач цього комп'ютера зможе прочитати закриту інформацію.
Приклад 12.4. Знищення змінних сесії
У файл secret_info.php додамо строчку для виходу на головну сторінку:
<?php
// ... php код
?>
<html>
<head><title>Secret info</title></head>
// тут розташовується секретна інформація :)
<а href="index.php">На главную</a>
</html>
Приклад 12.4a. secret_info.php
В Index.php знищимо логін і пароль, введений раніше:
<?
session_start();
session_unregister('passwd');
// знищуємо пароль
unset($_SESSION['login']);
// знищуємо логін
print_r($_SESSION);
// виводимо глобальні змінні сесії
?>
<html>
<head><title>My home page</title></head>
// домашня сторіночка
</html>
Приклад 12.4b. Index.php
Тепер, щоб потрапити на секретну сторінку, потрібно буде знову вводити логін і пароль.
Для того, щоб скинути значення всіх змінних сесії, можна використовувати функцію session_unset();
Знищити поточну сесію цілком можна командою session_destroy(); Вона не скидає значення глобальних змінних сесії і не видаляє cookies, а знищує всі дані, асоційовані з поточною сесією.
<?
session_start(); // ініціалізували сесію
$test = "змінна сесії";
$_SESSION['test']= $test;
// реєструємо змінну $test.
// якщо register_globals=on то можна використовувати session_register('test');
print_r($_SESSION);
// виводимо всі глобальні змінні
echo session_id();
// виводимо ідентифікатор сесії
echo "<hr>";
session_unset();
// знищуємо всі глобальні змінні сесії
print_r($_SESSION);
echo session_id();
echo "<hr>";
session_destroy(); // знищуємо сесію
print_r($_SESSION);
echo session_id();
?>
Приклад 12.5. Знищення сесії і глобальних змінних
В результаті роботи цього скрипта будуть виведено три рядки: в першій - масив з елементом test і його значенням, а також ідентифікатор сесії, в другій - порожній масив і ідентифікатор сесії, в третій - порожній масив. Таким чином, видно, що після знищення сесії знищується і її ідентифікатор, і ми більше не можемо ні реєструвати змінні, ні взагалі проводити які-небудь дії з сесією.
Взагалі кажучи, слід розуміти, що використовування механізму сесій не гарантує повної безпеки системи. Для цього потрібно вживати додаткові заходи. Звернемо увагу на проблеми з безпекою, які можуть виникнути при роботі з сесіями і, зокрема, з тими програмами, що ми написали.
По-перше, небезпечно передавати туди-сюди пароль, його можуть перехопити. Крім того, ми зареєстрували його як глобальну змінну сесії, значить, він зберігся в cookies на комп'ютері-клієнті. Це теж погано. І взагалі, паролі і логіни по-хорошому повинні зберігатися в базі даних. Хай інформація про користувачів зберігається в базі даних "test" (в таблиці "users"), а ми маємо до неї доступ під логіном my_user і паролем my_passwd.
По-друге, що робити, якщо хтось написав скрипт підбору пароля для секретної сторінки? В цьому випадку на сторінку авторизації багато раз повинен стукатися якийсь сторонній скрипт. Тому потрібно просто перевіряти, чи з нашого сайту прийшов запит на авторизацію, і якщо ні, то не пускати його далі. Адресу сторінки, з якою надійшов запит, можна одержати за допомогою глобальної змінної $_SERVER['HTTP_REFERER']). Хоча, звичайно, якщо за злом сайту узялися всерйоз, то значення цієї змінної теж підмінять (наприклад, за допомогою того ж PHP). Проте перевірку її значення можна вважати одним з найважливіших кроків на шляху до забезпечення безпеки свого сайту.
<?
session_start();
// створюємо нову сесію або відновлюємо поточну
$conn = mysql_connect("localhost", "my_user","my_passwd");
// встановлюємо з'єднання з сервером БД
mysql_select_db("test");
// вибираємо робочу базу даних
$SERVER_ROOT = "http://localhost/~vasia/tasks/sessions/";
/* за допомогою регулярного виразу $SERVER_ROOT і функції eregi перевіряємо чи починається адреса скрипта, з рядка $SERVER_ROOT (як у нас) */
if(eregi("$SERVER_ROOT" == $_SERVER['HTTP_REFERER'])){
// якщо так, то робимо майже те ж, що і раніше, пароль реєструвати не будемо
if (!isset($_POST['go'])){
echo "<form method=POST >
Login: <input type=text name=login>
Password: <input type=password name=passwd>
<input type=submit name=go value=Go>
</form>";
}else {
/* запит до бази даних: вибираємо з таблиці users login, який співпадає з переданим за запитом, причому пароль у нього теж повинен співпасти з введеним користувачем. Якщо цього нема, то вважаємо, що логін і пароль введені невірно */
$sql = "SELECT login FROM users
WHERE login='" . $_POST['login'] . "' AND passwd='" . $_POST['passwd'] . "';";
$q = mysql_query($sql,$conn); // відправляємо запит до БД
$n = mysql_num_rows($q); // число рядків відповідає на запит
if (!$n==0){
$_SESSION['user_login']=$_POST['login'];
// реєструємо змінну login
Header("Location: secret_info.php");
// перенаправляємо на сторінку secret_info.php
}else echo "Невірні дані, спробуйте ще раз<br>";
}
print_r($_SESSION); // виводимо всі змінні сесії
}
?>
Лістинг 12.6. authorize.php
Неначебто перші дві проблеми вирішено. Але є ще одна. Що робити, якщо хакер просто допише в рядок запиту значення якої-небудь глобальної змінної (наприклад, логіна)? Взагалі це можливо, тільки якщо register_globals=On. Просто інакше ми використовуємо для роботи з глобальними змінними масив $_SESSION і з ним такі фокуси не проходять. Все ж таки спробуємо розв'язати і цю проблему. Для цього потрібно очистити рядок запиту перед тим, як порівнювати значення параметрів. Тобто спочатку скинемо значення $user_login. Потім дану змінну потрібно знову зареєструвати, але не як нову, а як вже існуючу. Для цього знак долара при реєстрації не опускається. Ось що вийшло:
<?php
unset($user_login); // знищуємо змінну
session_start(); // створюємо нову сесію або відновлюємо поточну
session_register($user_login);
// реєструємо змінну як вже існуючу
if (!($user_login=="pit")) // перевіряємо логін
Header("Location: authorize.php");
// якщо помилка, то перенаправляємо на сторінку авторизації
?>
<html>
<head><title>Secret info</title></head>
... // тут розташовується секретна інформація :)
</html>
Лістинг 12.7. secret_info.php
Висновок
Отже, ми познайомилися з сесіями і основними способами роботи з ними, проблемами, що виникають при їх використовуванні, і можливими рішеннями цих проблем. Сподіваюся, що після прочитання лекції читачам стало ясно, наскільки зручні і прості у використовуванні сесії, а приведені приклади стануть в нагоді на практиці.