Накратко: За сигурността на сесиите във PHP са важни четири неща: сигурни настройки на бисквитките, нов ID на сесията след всяко влизане, разделени и защитени места за съхранение и кратки срокове. Заради PHP2Shell освен това session.upload_progress.enabled=0 трябва да е зададена на страната на сървъра, докато няма причина за тази функция.
Защо сесиите са ценна цел
Сесията помни, че потребителят е влязъл. Технически браузърът получава за това случаен ID на сесията в бисквитка, а сървърът съхранява съответните данни. Който знае този ID, е за приложението същият потребител, без парола и без втори фактор.
За операторите това означава: слабо управление на сесиите може да обезсмисли защитата на входа, дори паролите и двуфакторното удостоверяване да са реализирани чисто. Особено критично е това при административни зони, клиентски акаунти и портали с лични данни.
Типичните рискове са прихващането на ID на сесията (Session Hijacking), подхвърлянето на известен ID преди входа (Session Fixation) и прекалено дълго валидни сесии на споделени компютри.
Сигурни основни настройки в php.ini
Най-важните стойности могат да се зададат централно в php.ini или за всеки уебсайт във FPM пула. В пула използвайте php_admin_value или php_admin_flag, за да не може приложението да презапише стойностите.
; Основно заздравяване на сесиите
session.use_strict_mode = 1
session.use_only_cookies = 1
session.use_trans_sid = 0
session.cookie_httponly = 1
session.cookie_secure = 1
session.cookie_samesite = "Lax"
session.cookie_lifetime = 0
session.gc_maxlifetime = 1800
session.name = "APPSESSID"
; Съобщение на php.net за PHP2Shell, след това рестартирайте всички worker процеси
session.upload_progress.enabled = 0
use_strict_mode гарантира, че PHP не приема зададени от браузъра, непознати ID. cookie_httponly пречи на JavaScript да чете ID, което ограничава последствията от Cross-Site Scripting. cookie_secure изпраща бисквитката само през HTTPS. SameSite=Lax затруднява Cross-Site заявки с валидна сесия; за чисто административни зони може да подхожда Strict.
Срокът от 1800 секунди е примерна стойност. Определете го според нуждата от защита: по-кратък за администриране и клиентски данни, по-дълъг само там, където е функционално необходимо.
Имайте предвид, че gc_maxlifetime определя само от кога старите данни на сесиите могат да се изтриват. При някои дистрибуции файловете на сесиите не се почистват от самия PHP, а от редовна системна задача. Проверете тогава коя стойност използва тази задача, за да действа Вашата настройка наистина. За излизането след неактивност освен това не разчитайте само на функцията за почистване, а проверявайте момента на последното действие в самото приложение, както е показано в следващия пример.
Подновяване на ID на сесията след входа
Най-важното правило в кода е ID на сесията да се генерира наново при всяка промяна на правата, т.е. при вход, при преминаване в административен режим и при изход. Следният пример показва чист основен модел с password_verify():
<?php
declare(strict_types=1);
session_start();
function anmelden(PDO $db, string $email, string $passwort): bool
{
$stmt = $db->prepare('SELECT id, passwort_hash FROM benutzer WHERE email = :email');
$stmt->execute(['email' => $email]);
$nutzer = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$nutzer || !password_verify($passwort, $nutzer['passwort_hash'])) {
return false; // едно и също съобщение за грешен адрес и грешна парола
}
session_regenerate_id(true); // нов ID, старият се отхвърля
$_SESSION['benutzer_id'] = (int) $nutzer['id'];
$_SESSION['letzte_aktion'] = time();
return true;
}
function pruefeLeerlauf(int $sekunden = 900): void
{
if (isset($_SESSION['letzte_aktion']) && time() - $_SESSION['letzte_aktion'] > $sekunden) {
abmelden();
return;
}
$_SESSION['letzte_aktion'] = time();
}
function abmelden(): void
{
$_SESSION = [];
$p = session_get_cookie_params();
setcookie(session_name(), '', [
'expires' => time() - 3600,
'path' => $p['path'],
'domain' => $p['domain'],
'secure' => $p['secure'],
'httponly' => $p['httponly'],
'samesite' => $p['samesite'],
]);
session_destroy();
}
Допълнително защитавайте формулярите, които променят нещо, с CSRF токен. Той се генерира с bin2hex(random_bytes(32)), записва се в сесията и при изпращане се сравнява с hash_equals().
Места за съхранение и разделяне на клиентите
По подразбиране PHP съхранява сесиите като файлове в обща директория. На сървър с няколко уебсайта това може да означава, че компрометиран уебсайт може да чете сесиите на друг. Затова към всеки уебсайт принадлежи собствен пул със собствен потребител и собствен път за сесиите.
; във FPM пула на съответния уебсайт
php_admin_value[session.save_path] = /var/lib/php/sessions/kunde-a
Директорията трябва да принадлежи на потребителя на пула и да има права 0700. Как допълнително да изолирате уебсайтовете един от друг, описва наръчникът open_basedir и изолация за всеки уебсайт. Ако сесиите се съхраняват в Redis или в база данни, важат същите принципи: отделен достъп за всяко приложение, никакъв отворен мрежов достъп.
Сигурност на сесиите във PHP и PHP2Shell
Уязвимостите на PHP2Shell CVE-2026-109618 и CVE-2026-109619 са в самия PHP. Като една от преходните мерки php.net посочва session.upload_progress.enabled=0 да се зададе в php.ini, във FPM пула или в конфигурацията на сървъра и след това всички worker процеси да се рестартират. Изрично не чрез ini_set() или .user.ini.
На 23.10.2026 последва съобщение за CVE-2026-115560. Възможно изпълнение на код там съществува само при необичайна комбинация от сериализация на сесиите, превключване чрез ini_set() преди session_start() и активен upload progress. Мярката е същата настройка, до следващата редовна версия с поправки.
Тази настройка е преходна мярка, а не пълна защита. Решаваща остава актуализацията до поправена версия на PHP и последващият рестарт на всички PHP worker процеси.
След промяната проверете коя стойност действително важи. Командният ред често показва друга конфигурация от уеб сървъра; показателна е защитена, после отново изтрита страница за проверка или самата конфигурация на пула.
Типични грешки със сесиите в PHP приложения
Много слабости възникват не от липсващи настройки, а от начина, по който приложенията използват сесиите. Тези модели трябва да запитате целенасочено в собствения си код и при доставчиците на услуги:
- Твърде много в сесията: Пароли, пълни платежни данни или големи масиви от данни не принадлежат в сесията. Съхранявайте само необходимото за съпоставянето, например ID на потребителя и роли.
- Права само от сесията: Който е администратор, трябва да се проверява спрямо базата данни при критични действия. Иначе отнетите права остават в сила до изтичането на сесията.
- ID на сесията в адреса: ID в URL попадат в логове, история на браузъра и данни за Referer.
use_only_cookiesиuse_trans_sid = 0го предотвратяват. - Липсващ край на сесията: Без timeout при неактивност администратор на споделен компютър остава влязъл неограничено.
- HTTPS зад прокси е разпознат грешно: Ако криптирането завършва при load balancer-а, приложението трябва да го знае. Иначе то евентуално задава бисквитки без флага
Secure.
След инцидент със сигурността към мерките спада и отхвърлянето на всички съществуващи сесии. Най-лесно става, като изтриете съдържанието на директорията за сесии или смените името на сесията. Всички потребители тогава трябва да влязат наново, а откраднатите сесии са безполезни.
Как конкретно помага PHP SOS?
- Проверяваме настройките на сесиите, флаговете на бисквитките и местата за съхранение на всички уебсайтове на сървъра и документираме действително ефективните стойности.
- Въвеждаме преходните мерки за PHP2Shell за всеки пул, рестартираме контролирано worker процесите и доказваме резултата.
- Разделяме уебсайтовете със собствени пулове и пътища за сесиите в рамките на заздравяването на сървъра.
- За собствени разработки проверяваме вход, изход и смяна на сесията в кода и препоръчваме конкретни промени.
Често задавани въпроси
Какво е Session Hijacking?
При Session Hijacking атакуващ превзема сесията на влязъл потребител, като се сдобие с неговия ID на сесията. С ID той е за приложението този потребител, без да знае паролата. Сигурни флагове на бисквитките и кратки срокове го затрудняват значително.
Защо ID на сесията трябва да се генерира наново след входа?
Ако ID остава същото преди и след входа, предварително известен или подхвърлен ID би могъл да продължи да важи след влизането. session_regenerate_id(true) генерира нов ID и отхвърля стария.
Мога ли да изключа session.upload_progress.enabled чрез ini_set()?
Не, според php.net това не е достатъчно. Настройката принадлежи в php.ini, във FPM пула или в конфигурацията на сървъра, не в ini_set() или .user.ini. След това всички PHP worker процеси трябва да се рестартират.
Къде да се съхраняват файловете на сесиите?
В директория извън webroot, която може да чете само потребителят на съответния PHP пул. На сървъри с няколко уебсайта всеки уебсайт трябва да има собствена директория за сесии.