Текуща ситуация: PHP2Shell. Две критични уязвимости в PHP (CVSS 9,2) позволяват злонамерен код без вход. Корекции от 21.10.2026 г.Какво да направите сега →

Защита на сесиите във PHP: сигурност на сесиите на практика

Сесиите държат потребителите влезли в системата и затова са ценна цел. Как да конфигурирате сигурно бисквитките, ID на сесиите и местата за съхранение и да приложите съобщението за PHP2Shell относно upload progress.

Актуалност 6 мин. четенеНаръчници

Накратко: За сигурността на сесиите във 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 пул. На сървъри с няколко уебсайта всеки уебсайт трябва да има собствена директория за сесии.

Източници

Незабавен контакт

Всеки час има значение. Пишете ни сега.

Опишете накратко системата, версията на PHP и какво сте забелязали. Ние се свързваме с Вас, уточняваме ситуацията и работим отдалечено, докато инцидентът бъде приключен и документиран.

Незабавна помощ