Накратко: За най-честите задачи по сигурността PHP носи готови, проверени функции: password_hash() за пароли, random_bytes() и random_int() за токени, htmlspecialchars() срещу cross-site scripting, подготвени заявки с PDO срещу SQL инжекция, hash_equals() за сигурни сравнения и разширението Sodium за криптография. Който използва последователно тези градивни елементи, избягва повечето пробиви в приложенията. Срещу грешки в самия интерпретатор на PHP, както при PHP2Shell, те не помагат; там помага само актуализацията.
Защо вградени функции за сигурност в PHP?
Да се пише код за сигурност сам, е податливо на грешки. Собствен хеш на пароли, самостоятелно направен генератор на токени или домашно измайсторен филтър за HTML изход често изглеждат правилни, а не са. Вградените функции са документирани, тествани и се поддържат с актуализациите на PHP.
За вземащите решения това означава: питайте Вашата агенция или Вашия екип за разработка не дали функциите за сигурност са писани сами, а дали се използват стандартните функции на езика. Таблицата дава преглед.
| Задача | Функция | Налична от | Важна бележка |
|---|---|---|---|
| Съхраняване на пароли | password_hash(), password_verify() |
PHP 5.5 | колона до 255 знака; bcrypt съкращава до 72 байта |
| Случайни токени | random_bytes(), random_int() |
PHP 7.0 | никога rand() или mt_rand() за тайни |
| Случайност обектно-ориентирано | Random\Randomizer |
PHP 8.2 | същата сигурност, по-гъвкав интерфейс |
| Екраниране на HTML изход | htmlspecialchars() |
открай време | флаговете по подразбиране от 8.1 екранират и единични кавички |
| Сравнение с постоянно време | hash_equals() |
PHP 5.6 | въвеждането от потребителя като втори аргумент |
| Съвременна криптография | Sodium (sodium_*) |
PHP 7.2 | вместо собствено криптиране |
| Тайни в stack trace | #[\SensitiveParameter] |
PHP 8.2 | стойността не се появява в trace |
| Проверка на въвеждани данни | filter_var() |
PHP 5.2 | проверява формата, не замества екранирането |
Пароли и токени
password_hash() създава осолен хеш, password_verify() го проверява. С PASSWORD_DEFAULT PHP използва в момента bcrypt; от PHP 8.4 коефициентът на разходите по подразбиране според ръководството е 12 вместо 10. Тъй като алгоритъмът по подразбиране може да се промени, ръководството препоръчва колона в базата данни с 255 знака. При bcrypt паролите се отрязват след 72 байта, което трябва да се има предвид при много дълги пропозиции за парола.
<?php
// Регистрация
$hash = password_hash($passwort, PASSWORD_DEFAULT);
// Вход, включително автоматично обновяване на стари хешове
if (password_verify($eingabe, $hash)) {
if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
$hash = password_hash($eingabe, PASSWORD_DEFAULT); // съхраняване наново
}
}
// Токен за връзка за възстановяване на парола
$token = bin2hex(random_bytes(32));
За сравнения на тайни, например на подпис, трябва да се използва hash_equals(). Обикновеното сравнение с === според ръководството може да издаде чрез времето за изпълнение от кое място две стойности се различават. По-подробно: Сигурно съхраняване на пароли в PHP и сигурни случайни числа и токени.
Екраниране на изхода и сигурни заявки към базата данни
Cross-site scripting възниква, когато въвеждани от потребители данни попаднат нефилтрирани в HTML. htmlspecialchars() превръща специалните знаци в HTML ентитита. От PHP 8.1 флаговете по подразбиране са ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401; преди това единичните кавички не се преобразуваха. Който трябва да поддържа по-стари версии, задава флаговете изрично.
SQL инжекцията се предотвратява с подготвени заявки. Стойностите се предават отделно от SQL текста и не могат да променят заявката:
<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();
echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');
Подробностите са в Предотвратяване на cross-site scripting в PHP и на страницата за SQL инжекция.
Проверката на въвеждани данни е втора, отделна стъпка. filter_var() установява дали една стойност има валиден формат, например имейл адрес или цяло число в разрешен диапазон. Това обаче не замества нито екранирането при изхода, нито подготвената заявка, защото и формално валидни стойности могат да съдържат специални знаци.
Тайните да не попадат в съобщенията за грешки
Подценявано изтичане на данни са stack trace. Ако във функция възникне грешка, PHP записва веригата от извиквания заедно с аргументите. Ако там има парола или API ключ, той попада в лога. От PHP 8.2 атрибутът #[\SensitiveParameter] отбелязва такива параметри; стойността им в trace се появява тогава само като обект SensitiveParameterValue.
<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
// ... проверка ...
throw new RuntimeException('Datenbank nicht erreichbar');
}
Според бележка в ръководството атрибутът действа само върху конкретната реализация, не върху метода на интерфейс. Затова го задавайте там, където кодът действително се изпълнява.
Сесии: сигурни настройки и нови стандарти
Сесиите са често срещана цел на атаки. Три настройки са централни: session.use_strict_mode=1 отхвърля идентификатори на сесии, които сървърът не е създал сам; session.cookie_httponly=1 предотвратява достъпа до бисквитката на сесията чрез JavaScript; session.cookie_samesite=Lax затруднява изпращането ѝ към чужди сайтове. Според приетия RFC точно тези три стойности стават стандарт с PHP 8.6. Задайте ги сами дотогава, а и след това изрично, за да не зависи конфигурацията от стойността по подразбиране.
session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1
Повече има в Защита на сесиите в PHP.
Какво вградените функции не постигат
Проектът PHP в политиката си за сигурност е ясен: PHP не е sandbox. Заобикалянето на open_basedir или disable_functions, злонамерените входни данни към unserialize() и непроверените аргументи към функции като mysqli_query() не се считат за уязвимости на PHP. За операторите това означава: тези настройки са полезно заздравяване, но не граница на сигурността, а unserialize() няма работа с чужди данни.
Втората точка засяга PHP2Shell. CVE-2026-109618 и CVE-2026-109619 според php.net позволяват изпълнение на код, преди или докато приложението Ви работи; при mod_php е показан експлойт, преди изобщо да се изпълни код на приложението. Никоя функция във Вашия код не може да го улови. Сигурното програмиране и актуалната версия на PHP са две отделни задължения и и двете трябва да бъдат изпълнени.
Как помага PHP SOS конкретно?
В одита на сигурността за PHP приложения проверяваме целенасочено:
- Работа с пароли и токени: Използват ли се
password_hash()иrandom_bytes(), или има собствени разработки? - Изходи и заявки: Къде въвежданите данни попадат нефилтрирани в HTML, SQL или команди на шела?
- Конфигурация на сесиите: Зададени ли са Strict Mode, HttpOnly, SameSite и Secure и не могат ли да се презаписват?
- Среда за изпълнение: Работи ли поддържана версия на PHP с корекциите от 21.10.2026 г.?
Често задавани въпроси
Как се съхраняват правилно паролите в PHP?
С password_hash() и PASSWORD_DEFAULT, а проверката е с password_verify(). Колоната в базата данни според ръководството трябва да побира до 255 знака, защото алгоритъмът по подразбиране може да се промени в бъдеще.
rand() подходящ ли е за токени?
Не. За токени, ключове и връзки за парола трябва да се използват random_bytes() или random_int(). Според ръководството те дават криптографски сигурни случайни стойности.
Откога Sodium е част от PHP?
Разширението Sodium за съвременна криптография е част от PHP от версия 7.2.0. По-старите версии можеха да го добавят само чрез PECL.
Тези функции защитават ли от PHP2Shell?
Не. Те защитават от грешки във Вашето приложение. PHP2Shell се намира в самия интерпретатор и се затваря само чрез актуализация на PHP, последвана от рестартиране на всички PHP процеси.
Променят ли се стандартите за сесии в PHP 8.6?
Да, според приетия RFC session.use_strict_mode и session.cookie_httponly се включват по подразбиране, а session.cookie_samesite се задава на Lax. Съществуващите конфигурации все пак трябва сами да зададете изрично.