# Валидиране и филтриране на вход в PHP с filter_var

> Валидиране на вход в PHP с filter_var и filter_input_array: числа, имейл, URL, булеви стойности, списъци с разрешени стойности и граници. С примери за код.

Източник: https://phpsos.de/bg/blog/eingaben-validieren-php/  
Актуалност: 25.10.2026  
Доставчик: PHP SOS, Спешна помощ за PHP и сигурност на сървъри. Незабавна помощ по WhatsApp: https://wa.me/491736494914?text=%D0%97%D0%B4%D1%80%D0%B0%D0%B2%D0%B5%D0%B9%D1%82%D0%B5%2C%20PHP%20SOS%2C%20%D0%B8%D0%BC%D0%B0%D0%BC%D0%B5%20%D0%BD%D1%83%D0%B6%D0%B4%D0%B0%20%D0%BE%D1%82%20%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%20%D1%81%20%D0%BD%D0%B0%D1%88%D0%B8%D1%8F%20PHP%20%D1%81%D1%8A%D1%80%D0%B2%D1%8A%D1%80.%20%D0%A4%D0%B8%D1%80%D0%BC%D0%B0%3A%20%E2%80%A6%20%D0%A3%D0%B5%D0%B1%D1%81%D0%B0%D0%B9%D1%82%3A%20%E2%80%A6

**Накратко:** Входа в PHP валидирате с `filter_var()` или `filter_input_array()` и ясни правила за всяко поле: тип, диапазон, дължина и разрешени стойности. Което не отговаря, се отхвърля, а не се преправя мълчаливо. Валидирането е важно за чисти данни и по-малка повърхност за атака, но не замества нито Prepared Statements срещу SQL инжекция, нито кодирането според контекста срещу XSS.

## Защо „филтрирай всичко“ не е концепция

В много по-стари PHP проекти в началото на всеки файл има цикъл, който прекарва всички стойности от `$_GET` и `$_POST` през функция за почистване. Това изглежда сигурно. На практика понякога премахва твърде много, например апострофи във фамилни имена, а понякога твърде малко, защото една и съща функция трябва да важи едновременно за SQL, HTML и имена на файлове.

Смисленото валидиране работи по друг начин: то пита за всяко поле какво е разрешено там. Количеството в поръчката е цяло число между 1 и горна граница. Полето за сортиране е едно от три фиксирани стойности. Датата е в определен формат и след друга дата. Всичко останало се отхвърля.

OWASP разграничава две нива: **синтактична** проверка дали стойността има правилната форма и **семантична** проверка дали има смисъл в контекста. И двете принадлежат на сървъра; проверките в браузъра служат само за удобство.

## Какви филтри носи PHP

Разширението Filter е включено в PHP по подразбиране. Най-важните филтри за валидиране според наръчника на PHP:

| Филтър | Проверява | Важни опции и бележки |
|---|---|---|
| `FILTER_VALIDATE_INT` | цяло число | `min_range`, `max_range` |
| `FILTER_VALIDATE_FLOAT` | число с десетични знаци | `decimal` за разделителния знак |
| `FILTER_VALIDATE_BOOL` | булева стойност | с `FILTER_NULL_ON_FAILURE` се различава от „невярно“ |
| `FILTER_VALIDATE_EMAIL` | формата на имейл адрес | не доказва, че пощенската кутия съществува |
| `FILTER_VALIDATE_URL` | формата на URL | проверете схемата сами, без интернационализирани домейни |
| `FILTER_VALIDATE_IP` | IP адрес | флагове за частни и запазени диапазони |
| `FILTER_VALIDATE_REGEXP` | собствен шаблон | ограничете шаблона с `^` и `$` |

Филтърът за почистване `FILTER_SANITIZE_STRING` е остарял от PHP 8.1. Който още го ползва, трябва да премине валидирането към подходящи филтри, а за изхода да използва `htmlspecialchars()`.

## filter_var в PHP: как да проверите чисто един формуляр

С `filter_input_array()` цял формуляр може да се провери наведнъж спрямо дефиниция. Непознатите полета не се вземат:

```php
<?php
declare(strict_types=1);

$regeln = [
    'menge'   => ['filter' => FILTER_VALIDATE_INT,
                  'options' => ['min_range' => 1, 'max_range' => 50]],
    'email'   => FILTER_VALIDATE_EMAIL,
    'agb'     => ['filter' => FILTER_VALIDATE_BOOL, 'flags' => FILTER_NULL_ON_FAILURE],
    'versand' => ['filter' => FILTER_VALIDATE_REGEXP,
                  'options' => ['regexp' => '/^(standard|express)$/']],
];

$daten  = filter_input_array(INPUT_POST, $regeln) ?? [];
$fehler = [];

foreach ($regeln as $feld => $_) {
    if (!array_key_exists($feld, $daten) || $daten[$feld] === false || $daten[$feld] === null) {
        $fehler[] = $feld;
    }
}

if ($fehler !== []) {
    http_response_code(422);
    // Изведете съобщение за грешка за всяко поле, не обработвайте входните данни
}
```

Обърнете внимание на стриктните сравнения с `===`: валидна стойност `0` или празен низ не бива да се бърка с неуспех. Защо хлабавите сравнения тук са опасни, обяснява [Type Juggling: хлабавите сравнения като риск за сигурността](https://phpsos.de/bg/blog/type-juggling-php/). Как PHP изобщо предоставя данните от заявката, описва [Superglobals: $_GET, $_POST, $_FILES и др.](https://phpsos.de/bg/php/superglobals/).

## Клопки при имейл, URL и свободен текст

Някои филтри правят по-малко, отколкото подсказва името им:

1. **URL адреси:** `FILTER_VALIDATE_URL` приема и други схеми освен http и https. Проверете схемата след това с `parse_url()` спрямо фиксиран списък. Ако URL адресът се извлича от сървъра, важат допълнителни правила, вижте [Предотвратяване на Server-Side Request Forgery в PHP](https://phpsos.de/bg/blog/ssrf-in-php/).
2. **Имейл адреси:** Филтърът проверява формата, а не притежанието. Който трябва да е сигурен, изпраща връзка за потвърждение.
3. **Свободен текст:** Имена, съобщения и адреси трудно могат смислено да се ограничат по знаци. Проверявайте дължината и кодирането на знаците, например с `mb_check_encoding($wert, 'UTF-8')`, и разчитайте при изхода на кодиране.
4. **Регулярни изрази:** Без `^` и `$` шаблонът проверява само дали се среща някъде в текста, а не дали цялата стойност съвпада.
5. **Масиви вместо низове:** Атакуващият може да изпрати масив вместо текст. Без проверка на типа това води до предупреждения или неочаквано поведение.

> **Съвет:**
> За фиксирани стойности за избор като сортиране, език или начин на доставка списък с разрешени стойности с `in_array($wert, $erlaubt, true)` или израз `match` е по-прост и по-строг от всеки регулярен израз.

Друга честа грешка засяга числа със значение, например цени, количества или отстъпки. Това, че една стойност е валидно число, не казва нищо дали е позволена в контекста. Отрицателно количество, отстъпка над стойността на стоката или цена, взета от формуляра вместо от базата данни, са класически логически грешки. Цените, правата и стойностите на състоянието затова винаги принадлежат на сървъра и никога в скрити полета на формуляри.

## JSON, уебхукове и импорти: валидиране отвъд формулярите

Много входове днес вече не са формуляри, а JSON заявки от приложения, уебхукове от платежни услуги или файлове за импорт от доставчици. И там важи: първо се чете безопасно, после се проверява. OWASP съветва да се ограничат размерът и дълбочината на вложеност, преди данните изобщо да се прочетат. В PHP `json_decode()` с максимална дълбочина и `JSON_THROW_ON_ERROR` поема част от това:

```php
<?php
$roh = file_get_contents('php://input', false, null, 0, 65536); // най-много 64 KB
try {
    $daten = json_decode($roh, true, 16, JSON_THROW_ON_ERROR);
} catch (JsonException) {
    http_response_code(400);
    exit;
}
if (!is_array($daten) || !is_string($daten['bestellnummer'] ?? null)) {
    http_response_code(422);
    exit;
}
```

При уебхуковете преди проверката на съдържанието стои въпросът дали съобщението наистина идва от посочената услуга. Ако услугата предлага подпис, проверете го и го сравнете с `hash_equals()`. При файлове за импорт проверявайте всеки ред като формуляр и при грешки прекъсвайте чисто, вместо да приемате половин записи.

Съобщенията за грешка трябва да назовават засегнатото поле, но да не връщат отхвърлената стойност нефилтрирана. Иначе в самото съобщение за грешка възниква ново място за изход, което отново трябва да се кодира.

## Какво валидирането не може да постигне

Валидирането намалява повърхността за атака, но според OWASP не е главната защита срещу инжекции. Фамилно име с апостроф е валидно и въпреки това трябва да стигне безопасно до базата данни. Това става само с Prepared Statements, както показва наръчникът [Предотвратяване на SQL инжекция в PHP](https://phpsos.de/bg/ratgeber/sql-injection-php/). А валиден текст на коментар става безопасен едва с кодиране според контекста при изхода.

Правилната последователност следователно е: валидиране при получаването, параметризиране при обработката, кодиране при изхода. Допълнително преди това стоят ограниченията на размера: заявки, които са далеч твърде големи, уеб сървърът и PHP не бива изобщо да обработват изцяло. За това служат настройки като `post_max_size`, които след PHP2Shell така или иначе трябва да се прегледат.

## Как помага PHP SOS конкретно?

- Отчитаме всички входове на Вашето приложение: формуляри, интерфейси, качвания, импорти и уебхукове.
- Определяме за всяко поле какво е разрешено и заменяме общите функции за почистване с ясни проверки.
- Проверяваме дали достъпите до базата данни и изходите са защитени независимо от валидирането.
- Документираме правилата така, че Вашият екип да може да ги продължава при нови функции.

Систематична проверка описва услугата [Одит на сигурността и тест за проникване](https://phpsos.de/bg/leistungen/sicherheitsaudit-penetrationstest/).


## Често задавани въпроси

**Каква е разликата между валидиране и почистване?**  
Валидирането проверява дали една стойност отговаря на очакванията и в противен случай я отхвърля. Почистването променя стойността, например като премахва знаци. За решения, свързани със сигурността, отхвърлянето почти винаги е по-добро, защото мълчаливо променена стойност може да породи нови грешки.

**Все още ли може да се ползва FILTER_SANITIZE_STRING?**  
Филтърът е маркиран като остарял от PHP 8.1. Наръчникът на PHP препоръчва вместо това htmlspecialchars() при изхода. Съществуващият код трябва да преминете, защото остарели функции могат да отпаднат в бъдещи версии.

**Достатъчно ли е FILTER_VALIDATE_URL, за да направи връзките сигурни?**  
Не. Филтърът проверява само формата на адреса. Според наръчника валиден URL не е задължително да започва с http, затова схемата трябва да проверите сами спрямо списък с разрешени стойности като https. Интернационализирани имена на домейни филтърът отхвърля.

**Трябва ли да валидирам и данни от собствената си база данни?**  
При изхода те се смятат за ненадеждни и се кодират според контекста. Ново валидиране е смислено, когато данните първоначално са дошли отвън и сега се използват в нов контекст, например като име на файл или част от команда.

**Помага ли валидирането срещу PHP2Shell?**  
Не. PHP2Shell засяга обработката на заявки в интерпретатора на PHP, преди кодът на Вашето приложение изобщо да се изпълни. Срещу това помага само актуализацията на PHP със следващ рестарт на всички PHP процеси.

## Източници

- [Наръчник на PHP: константи на филтрите](https://www.php.net/manual/de/filter.constants.php)
- [OWASP Input Validation Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
