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

Предотвратяване на SQL инжекция в PHP

Как разработчиците надеждно изключват SQL инжекцията в PHP приложения с Prepared Statements, whitelist и минимални права за базата данни.

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

Накратко: SQL инжекцията в PHP предотвратявате надеждно, като никога не вграждате стойности в SQL текстове, а винаги ги предавате чрез Prepared Statements с placeholder-и. За имена на колони и таблици, които не могат да се предават като placeholder-и, важи фиксиран whitelist. Допълнително потребител на базата данни с минимални права ограничава щетите, ако все пак остане уязвимост.

Защо SQL инжекцията не бива да излиза от полезрението след PHP2Shell

През октомври 2026 г. много неща се въртят около PHP2Shell, две критични уязвимости в интерпретатора на PHP, поправени от 21.10.2026 в PHP 8.5.12, 8.4.27, 8.3.36 и 8.2.35. Много екипи през тези дни актуализираха сървъри и рестартираха процеси. Това е правилно и спешно.

Актуалното състояние на PHP обаче не казва нищо за собствения код. SQL инжекцията от години е сред най-честите слабости в уеб приложенията и в OWASP Top 10 е водена в категорията Injection. Който така или иначе планира проверки за сигурност, трябва да погледне и достъпите до базата данни на собственото си приложение.

Добрата новина: SQL инжекцията е една от малкото слабости, които почти напълно могат да се изключат с едно просто, последователно прилагано правило.

Как възниква SQL инжекцията в PHP?

SQL инжекцията възниква, когато приложение третира въведените от потребителя данни като част от SQL команда, вместо като чисти данни. Типично е съставянето на заявка от текстови части и променливи. Ако посетител може да определя съдържанието на такава променлива, при определени обстоятелства може да промени смисъла на командата.

Последствията вървят от изчитане на чужди данни на клиенти през промяна на цени или права до изтриване на цели таблици. В зависимост от базата данни и правата една уязвимост може да позволи и достъпи отвъд базата данни. Ако са засегнати лични данни, може да възникне задължение за уведомяване по ОРЗД.

Въвеждането не са само полетата на формулярите. Недоверени се смятат и URL параметрите, бисквитките, HTTP хедърите, качените файлове, отговорите на чужди интерфейси и вече записаните данни от собствената база данни.

Prepared Statements с PDO и mysqli

Prepared Statements изпращат SQL командата и стойностите отделно към базата данни. Стойностите никога не се интерпретират като част от командата. С PDO сигурна заявка изглежда така:

<?php
$pdo = new PDO($dsn, $benutzer, $passwort, [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_EMULATE_PREPARES   => false,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);

$stmt = $pdo->prepare('SELECT id, name, email FROM kunden WHERE id = :id AND status = :status');
$stmt->execute([
    'id'     => (int) $_GET['id'],
    'status' => 'aktiv',
]);
$kunde = $stmt->fetch();

Опцията PDO::ATTR_EMULATE_PREPARES => false гарантира, че базата данни използва истински Prepared Statements, доколкото драйверът го поддържа. С mysqli важи същият принцип:

<?php
$stmt = $mysqli->prepare('SELECT id, name FROM produkte WHERE kategorie = ? AND preis <= ?');
$stmt->bind_param('sd', $kategorie, $maxPreis);
$stmt->execute();
$ergebnis = $stmt->get_result();

Дори списъците за IN (...) могат да се изобразят сигурно, като създадете за всяка стойност собствен placeholder и предадете стойностите отделно.

<?php
$ids = array_map('intval', $ausgewaehlteIds);
$platzhalter = implode(',', array_fill(0, count($ids), '?'));
$stmt = $pdo->prepare("SELECT id, name FROM produkte WHERE id IN ($platzhalter)");
$stmt->execute($ids);

Особени случаи: имена на колони, сортиране и суров SQL

Placeholder-ите работят само за стойности, не за имена на таблици, имена на колони или посоки на сортиране. Именно тук на практика възникват много уязвимости, например при сортируеми таблици или филтри. Решението е фиксиран списък от разрешени стойности:

<?php
$erlaubteSpalten = ['name' => 'name', 'preis' => 'preis', 'datum' => 'erstellt_am'];
$spalte   = $erlaubteSpalten[$_GET['sort'] ?? ''] ?? 'name';
$richtung = (($_GET['dir'] ?? '') === 'desc') ? 'DESC' : 'ASC';

$stmt = $pdo->query("SELECT id, name, preis FROM produkte ORDER BY $spalte $richtung");

Фреймуърците и CMS носят собствени инструменти. В WordPress тази задача поема $wpdb->prepare(), в Laravel и Symfony Query Builder и ORM. Щом там използвате суров SQL, например през функции с „raw“ в името, отново важат правилата на този раздел. Указания за Laravel ще намерите на страницата Защита на приложения с Laravel.

Съвет

Търсете целенасочено в кода SQL текстове, в които се вмъкват променливи. Лесно начало е търсене на query( и ->raw в комбинация с $_GET, $_POST или $_REQUEST.

Откриване на SQL инжекция в съществуващ код

При нов софтуер правилото може да се спазва от самото начало. По-трудно е при приложения, които са растели с години. Там си струва систематично търсене на места, където SQL текстове се съставят с променливи:

# SQL команди, в които се вмъкват променливи
grep -rnE "(SELECT|INSERT|UPDATE|DELETE)[^;]*\\\$[a-zA-Z_]" src/ --include="*.php" | head -50
# Директно използване на данни от заявката във функции за заявки
grep -rnE "(query|exec|raw)\s*\(.*\\\$_(GET|POST|REQUEST|COOKIE)" src/ --include="*.php"

Не всяко съвпадение е уязвимост, например когато променливата съдържа само фиксирано име на таблица. Всяко място обаче трябва веднъж да се провери съзнателно и при нужда да се преведе на Prepared Statements. Инструментите за статичен анализ на кода помагат тези модели да се разпознават трайно в build pipeline, така че новите грешки изобщо да не стигат до продукция.

Ограничаване на щетите: права, съобщения за грешки, проверка

Сигурният код е първата линия на защита. Допълнителни нива ограничават щетите, ако все пак някоя уязвимост бъде пропусната:

  • Минимални права: Потребителят на базата данни на уебсайта обикновено се нуждае само от SELECT, INSERT, UPDATE и DELETE върху собствената база данни, без права за създаване на потребители или за достъп до други бази данни.
  • Никакви съобщения за грешки навън: display_errors = Off в php.ini. Грешките на базата данни принадлежат в лога, не в браузъра.
  • Разделяне за всяко приложение: Всеки уебсайт получава собствен потребител на базата данни.
  • Поддържане на зависимостите актуални: Уязвимости има и в библиотеките, както показва наръчникът composer audit.
  • Проверка от специалисти: Прегледът на кода и автоматизираният анализ откриват много места, а тестът за проникване показва какво е действително достъпно отвън.
-- Пример: потребител на базата данни само с необходимите права
CREATE USER 'shop_app'@'localhost' IDENTIFIED BY '<starkes Passwort>';
GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'shop_app'@'localhost';

Сродна уязвимост засяга извеждането на данни в браузъра. Как да я защитите, обяснява наръчникът Предотвратяване на Cross-Site Scripting в PHP.

Как конкретно помага PHP SOS?

  • Проверяваме систематично достъпите до базата данни на Вашето приложение за несигурни заявки.
  • Приоритизираме находките според достъпността и засегнатите данни и показваме конкретни корекции.
  • Привеждаме правата за базата данни и обработката на грешки на сигурно ниво.
  • След корекцията тестваме отново и документираме резултата.

Независима проверка описва услугата Одит на сигурността и тест за проникване.

Често задавани въпроси

Достатъчно ли е да обработвам въвеждането с addslashes или real_escape_string?

Не, това е податливо на грешки. Escaping зависи от набора от знаци и от контекста и лесно се забравя на някое място. Prepared Statements принципно разделят командата от данните и затова са препоръчваният път.

Защитава ли ORM автоматично от SQL инжекция?

До голяма степен, докато ползвате функциите за заявки на ORM. Щом пишете суров SQL или влагате стойности в сортирания и имена на колони, важат същите правила както без ORM.

Помага ли Web Application Firewall срещу SQL инжекция?

WAF може да улови известни модели и да спечели време. Тя обаче не замества сигурния код, защото правила, базирани на данните от заявките, могат да се заобиколят и понякога засягат законно въвеждане.

Затваря ли актуализацията на PHP срещу PHP2Shell и SQL инжекционните уязвимости?

Не. PHP2Shell засяга интерпретатора на PHP. SQL инжекцията е грешка в кода на приложението и остава, независимо коя версия на PHP работи.

Източници

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

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

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

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