# Предотвратяване на Server-Side Request Forgery (SSRF) в PHP

> Предотвратяване на SSRF в PHP: списъци с разрешени адреси, само HTTPS, без пренасочвания, проверка на разрешения IP и ограничаване на изходящия трафик. С cURL код.

Източник: https://phpsos.de/bg/blog/ssrf-in-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

**Накратко:** SSRF в PHP предотвратявате, като Вашето приложение извлича само адреси от фиксиран списък с разрешени стойности, единствено през HTTPS и без да следва пренасочвания. Разрешеният IP адрес се проверява спрямо частни и запазени диапазони и после се фиксира за връзката. Защитна стена за изходящия трафик гарантира, че вътрешните системи остават недостъпни и при грешка в кода.

## Когато собственият сървър прави заявки за непознати

Визуализации на връзки в чат, импорт на изображение на продукт чрез URL, уебхукове към системи на партньори, четец на емисии (feed reader) в CMS: много PHP приложения извикват адреси, които потребителят е въвел. Това е полезна функция и същевременно врата навътре.

При Server-Side Request Forgery, накратко SSRF, атакуващият принуждава приложението да изпрати заявка към цел по негов избор. Уловката: заявката идва от самия сървър. Така тя стига до системи, които отвън са недостъпни, например бази данни, интерфейси за администриране във вътрешната мрежа, услуги, които слушат само на localhost, или услугите за метаданни на облачните доставчици.

В OWASP Top 10 от 2021 г. SSRF беше отделна категория. В PHP проекти уязвимостта често се появява там, където някой бързо е изградил функция „добавяне на изображение чрез URL“.

## SSRF в PHP: кои функции извличат адреси

Не само cURL извлича адреси. Трябва да имате предвид следните места:

| Функция или област | Защо е релевантна |
|---|---|
| cURL (`curl_exec()`) | най-честият път, възможни са много протоколи |
| `file_get_contents()`, `fopen()`, `copy()` с URL | ползват URL wrapper-и, когато `allow_url_fopen` е активно |
| `getimagesize()` и библиотеки за изображения | могат да зареждат отдалечени файлове |
| Обработка на XML, SOAP клиенти | при определени обстоятелства зареждат външни ресурси |
| HTTP библиотеки като Guzzle | надграждат cURL или stream-ове, същите правила |

Ако Вашето приложение не се налага да отваря отдалечени файлове чрез файлови функции, изключете `allow_url_fopen` от страна на сървъра. Сродна тема, включването на файлове чрез пътища от заявката, разглежда наръчникът [Предотвратяване на Local и Remote File Inclusion](https://phpsos.de/bg/ratgeber/lfi-rfi/).

## Защитата в пет стъпки

OWASP препоръчва комбинация от мерки, всяка от които поотделно може да бъде заобиколена, но заедно издържат:

1. **Списък с разрешени стойности вместо свободен URL:** Най-добре потребителят избира само от известни цели или посочва единствено идентификатор, а приложението само изгражда адреса.
2. **Разрешете само HTTPS:** Изключете други протоколи като `file`, `gopher` или `ftp` в cURL, също и за пренасочвания.
3. **Не следвайте пренасочвания:** Иначе един разрешен адрес може да пренасочи към вътрешна цел.
4. **Проверете и фиксирайте разрешения адрес:** Разрешете името на хоста, проверете IP спрямо частни диапазони, loopback и link-local и използвайте точно този IP за връзката. Иначе второ DNS разрешаване може да даде друга цел.
5. **Ограничете мрежата:** Уеб сървърът може да достига навън само целите, от които се нуждае.

## Сигурен образец с cURL

Следващият пример реализира стъпки 1 до 4 за IPv4. Имената на хостове и последователността са примери; за IPv6 важат същите проверки.

```php
<?php
declare(strict_types=1);

function ladeVonPartner(string $url): string
{
    $teile = parse_url($url);
    if (!is_array($teile) || ($teile['scheme'] ?? '') !== 'https'
        || isset($teile['user']) || isset($teile['pass']) || isset($teile['port'])) {
        throw new InvalidArgumentException('Adresse nicht erlaubt.');
    }

    $host = strtolower($teile['host'] ?? '');
    $erlaubt = ['api.partner.example', 'bilder.partner.example'];
    if (!in_array($host, $erlaubt, true)) {
        throw new InvalidArgumentException('Ziel nicht erlaubt.');
    }

    $ip = gethostbyname($host);
    $oeffentlich = filter_var($ip, FILTER_VALIDATE_IP,
        FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE);
    if ($oeffentlich === false) {
        throw new RuntimeException('Ziel löst auf eine nicht erlaubte Adresse auf.');
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER  => true,
        CURLOPT_FOLLOWLOCATION  => false,
        CURLOPT_PROTOCOLS       => CURLPROTO_HTTPS,
        CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTPS,
        CURLOPT_RESOLVE         => [$host . ':443:' . $ip],
        CURLOPT_CONNECTTIMEOUT  => 3,
        CURLOPT_TIMEOUT         => 10,
    ]);
    $antwort = curl_exec($ch);
    if ($antwort === false) {
        throw new RuntimeException('Abruf fehlgeschlagen.');
    }
    return $antwort;
}
```

Някои подробности са важни. `gethostbyname()` при грешка връща името на хоста; последващата проверка на IP го улавя. Флаговете `FILTER_FLAG_NO_PRIV_RANGE` и `FILTER_FLAG_NO_RES_RANGE` според наръчника на PHP изключват, наред с другото, диапазоните 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8 и 169.254.0.0/16. От PHP 8.2 има и `FILTER_FLAG_GLOBAL_RANGE`, който допуска само глобално валидни адреси. `CURLOPT_RESOLVE` гарантира, че cURL използва точно проверения адрес, докато името на хоста и проверката на сертификата се запазват.

Още две точки ограничават какво научава атакуващият от една SSRF уязвимост. Не връщайте отговорите и съобщенията за грешка на извлечената цел нефилтрирани на потребителя: още разграничението между „връзката е отказана“ и „изтекло време“ издава кои вътрешни услуги съществуват. И ограничете времетраенето и размера на отговора, за да не може функцията да се използва за претоварване на собствения сървър. Уебхуковете към адреси, които клиентите сами въвеждат, най-добре изпращайте от опашка, а не по време на текущата заявка.

> **Важно:**
> Ако Вашето приложение наистина трябва да извлича произволни адреси, например за визуализации на връзки, изнесете тази функция в отделна услуга без достъп до вътрешната мрежа. Списък с разрешени стойности тогава не е възможен, а мрежовото разделяне е още по-важно.

## Мрежовото ниво като мрежа за безопасност

Проверките в кода могат да имат пропуски, например когато две библиотеки разчленяват един URL по различен начин. OWASP съветва такива несъответствия да се третират като отхвърляне и допълнително да се ограничава на ниво мрежа. Един уеб сървър навън обикновено се нуждае само от малко цели: източници на пакети, доставчици на плащания и доставки, пощенско реле, определени интерфейси.

Защитна стена за изходящия трафик помага двойно. Тя спира SSRF заявките към вътрешната мрежа и след пробив, например през непачната версия на PHP, затруднява зареждането на допълнителни инструменти. Как да изградите правилата, описва [Защитна стена за уеб сървъри: отваряйте само необходимите портове](https://phpsos.de/bg/blog/firewall-fuer-webserver/). Какви връзки сървърът всъщност изгражда, показва [Проверка на изходящите връзки на сървъра](https://phpsos.de/bg/blog/ausgehende-verbindungen-pruefen/).

В облачни среди към това спада заздравеният вариант на услугата за метаданни. OWASP посочва за AWS преминаването към IMDSv2 и изключването на IMDSv1.

## Откриване на SSRF в съществуващия код

Бърз старт е търсенето на места, където функциите за извличане получават стойности от заявката:

```bash
grep -rnE "(curl_init|file_get_contents|fopen|getimagesize|copy)\s*\(" src/ --include="*.php" | grep -E '\$_(GET|POST|REQUEST)|\$url'
grep -rn "CURLOPT_FOLLOWLOCATION" src/ --include="*.php"
```

Попаденията са задачи за проверка. Особено старателно трябва да прегледате функциите, които ползват администраторите: импорти, настройки на уебхукове, проверки за актуализации на добавки.

Лесно се пренебрегват генераторите на PDF, които превръщат HTML с вградени изображения или стилови таблици в документи. Ако част от този HTML идва от въведени от потребителя данни, например при фактури със свободно избрано лого, генераторът при определени обстоятелства извлича адреси, определени от потребителя. Проверете там настройките за зареждане на отдалечени ресурси и ги изключете, ако не са нужни.

## Как помага PHP SOS конкретно?

- Намираме всички места, където Вашето приложение извлича адреси, включително в добавките и библиотеките.
- Изграждаме централна функция за извличане със списък с разрешени стойности, ограничаване на протоколите и проверка на IP.
- Определяме правила за изходящия трафик и защитаваме услугите за метаданни в облачни среди.
- Проверяваме реализацията отвън и документираме разрешените цели.

Независима проверка на Вашето приложение описва [Одит на сигурността и тест за проникване](https://phpsos.de/bg/leistungen/sicherheitsaudit-penetrationstest/).


## Често задавани въпроси

**Кои PHP функции могат да бъдат злоупотребени за SSRF?**  
Всички, които извличат адрес от въведени от потребителя данни: cURL, file_get_contents и fopen с URL wrapper-и, функции за изображения и XML, които зареждат отдалечени файлове, както и библиотеки за уебхукове, емисии (feeds) или визуализации на връзки.

**Достатъчно ли е да блокирам вътрешните IP адреси?**  
Само това не. OWASP определя блокиращите списъци като лесни за заобикаляне и препоръчва списъци с разрешени стойности. Освен това приложението трябва да използва точно адреса, който е проверило, и да не следва пренасочвания.

**Защо SSRF е особено критична в облака?**  
Облачните доставчици предоставят на инстанциите вътрешни услуги за метаданни, през които понякога могат да се извлекат данни за достъп. Ако едно приложение може да изпраща заявки натам, атакуващият може чрез него да стигне до тези данни. Доставчиците предлагат заздравени варианти на тези услуги, които трябва да са включени.

**Какво дава защитната стена за изходящия трафик?**  
Тя ограничава до кои цели сървърът изобщо може да стигне. Дори проверка в кода да се провали, вътрешните системи остават недостъпни. Освен това след пробив затруднява зареждането на злонамерен код и изтичането на данни.

## Източници

- [OWASP Server-Side Request Forgery Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html)
- [Наръчник на PHP: константи на филтрите](https://www.php.net/manual/de/filter.constants.php)
- [Наръчник на PHP: константи на cURL](https://www.php.net/manual/de/curl.constants.php)
