Накратко: Защитната стена за уеб сървър допуска входящо само TCP 80 и 443, при HTTP/3 допълнително UDP 443, и SSH само от известни мрежи. Всичко останало се отхвърля: бази данни, кешове, PHP-FPM и административни интерфейси не принадлежат в интернет. Първо проверете с ss -tulpn какво слуша, след това изградете набор от правила със стандарт „отхвърляне“ и тествайте резултата отвън. Ограничаването на изходящите връзки допълнително ограничава последиците от пробив.
Забравените врати до входната врата
При PHP2Shell погледът беше насочен към самия уеб сървър, към портове 80 и 443. Те трябва да са отворени, иначе няма сайт. При отчитане на наличното след такива уязвимости обаче често се забелязва нещо друго: до входната врата стоят отворени и други врати. База данни слуша на всички мрежови интерфейси, пул на PHP-FPM на порт 9000, кеш услуга без парола, административен интерфейс от фазата на настройката.
Всяка от тези врати е собствена повърхност за атака със собствени уязвимости. Защитна стена, която допуска само необходимото, ги затваря с няколко правила. Това е една от най-ефективните мерки при осигуряването на собствен сървър, както описва и страницата Самостоятелно осигуряване на root сървър и vServer.
Какво да е отворено и какво не
Принципът е списък с разрешени: всичко е затворено, а достъпни са само съзнателно разрешени услуги. Следният преглед помага при подреждането.
| Услуга | Типичен порт | Достъпна ли е отвън? |
|---|---|---|
| HTTP и HTTPS | TCP 80, TCP 443 | да |
| HTTP/3 (QUIC) | UDP 443 | само ако се ползва |
| SSH | TCP 22 | само от известни мрежи или през VPN |
| Хостинг панел | според продукта | само от известни мрежи |
| MySQL/MariaDB, PostgreSQL | TCP 3306, TCP 5432 | не |
| Redis, Memcached | TCP 6379, TCP 11211 | не |
| PHP-FPM | TCP 9000 или Unix сокет | не, по-добре само сокет |
| Пощенски сървър | TCP 25, 465, 587, 993 | само ако сървърът получава или предоставя поща |
Услуга, която не бива да е достъпна отвън, трябва допълнително да слуша само на 127.0.0.1 или на Unix сокет. Тогава тя остава недостъпна дори при повреда в защитната стена. За административните достъпи подхождат разрешаване по IP адреси или VPN; подробности има в статията Защита на административните зони чрез IP, VPN или допълнителна защита.
Стъпка 1: установете какво слуша
Преди да пишете правила, си създайте общ поглед. Следните команди само четат:
# Всички слушащи TCP и UDP портове с процес
sudo ss -tulpn
# Текущ набор от правила на nftables
sudo nft list ruleset
# Състояние на ufw, ако се ползва
sudo ufw status verbose
В изхода на ss обърнете внимание на адреса пред порта. 0.0.0.0 или [::] означава: услугата слуша на всички интерфейси. 127.0.0.1 означава: само локално. Запишете за всяка услуга дали трябва да е достъпна отвън. Този списък е същевременно част от Вашия инвентар на сървъра.
Защитна стена за уеб сървър с nftables
nftables е актуалният пакетен филтър под Linux. Следният набор от правила се основава на примера в официалното wiki на nftables и добавя разрешаване на SSH само от мрежа за администриране. Адресите са примери от документацията и трябва да се заменят.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set verwaltung_v4 {
type ipv4_addr
flags interval
elements = { 203.0.113.0/28 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state vmap { established : accept, related : accept, invalid : drop }
iifname lo accept
# IPv6 се нуждае от откриване на съседи
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
icmp type echo-request limit rate 5/second accept
icmpv6 type echo-request limit rate 5/second accept
# Уебсайт
tcp dport { 80, 443 } accept
# само при HTTP/3:
# udp dport 443 accept
# SSH само от мрежата за администриране
ip saddr @verwaltung_v4 tcp dport 22 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}
Проверете набора от правила преди зареждане с nft -c -f /etc/nftables.conf. Първият ред flush ruleset изтрива всички съществуващи правила, и такива, записани от Docker или Fail2ban. На сървъри с Docker или други услуги, които задават собствени правила, затова адаптирайте набора от правила, вместо да го заменяте изцяло. Как Fail2ban взаимодейства със защитната стена, е описано в Настройка на Fail2ban за уеб сървъри.
Който предпочита по-просто, ползва под Ubuntu ufw. Важен е редът: първо разрешаването на SSH, после включване.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow proto tcp from 203.0.113.0/28 to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Променяйте правилата на защитната стена на отдалечени сървъри само с втори път за достъп, например конзолата на Вашия доставчик. Грешно правило за SSH иначе Ви блокира самия Вас.
Изходящи връзки: подценяваната половина
Повечето защитни стени позволяват изходящо всичко. За уеб сървър това е щедро. След пробив атакуващите използват изходящи връзки, за да зареждат инструменти, да изнасят данни или да получават команди. Процеси на уеб сървъра или PHP, които установяват неочаквани връзки, затова се смятат за предупредителен сигнал.
Ограничаването на изходящите връзки е по-трудоемко, защото всяка легитимна връзка трябва да е позната: източници на пакети за актуализации, платежни услуги, интерфейси, изпращане на поща, сървъри за време. Прагматичен път е първо да се протоколират изходящите връзки, след това да се състави списък с разрешени и едва тогава да се блокира. Как да проверите съществуващите връзки, описва Проверка на изходящите връзки на сървър; кои други следи сочат към пробив, е в Вече компрометиран ли съм?.
Накрая тествайте отвън, от друг компютър и само срещу Вашите собствени системи, кои портове са действително достъпни. Едва този тест показва дали Docker, защитната стена на доставчика и правилата на сървъра заедно дават желаната картина.
Извод: малко правила, голям ефект
Защитната стена на уеб сървър не трябва да е сложна. Уеб портовете са отворени, администрирането само от известни мрежи, всичко останало отхвърлено: с тези основни правила базите данни, кешовете и забравените услуги изчезват от интернет. Това е една от мерките с най-добро съотношение между усилие и полза.
Срещу уязвимости в уеб сървъра или в самия PHP защитната стена не помага, защото точно тези портове трябва да останат отворени. Силата ѝ е да поддържа останалата повърхност за атака малка и чрез ограничаване на изходящите връзки да овладява последиците от пробив.
Решаваща е поддръжката. Всяка нова услуга, всеки контейнер и всяка промяна при доставчика може да измести картината. Затова повтаряйте проверката отвън след всяка по-голяма промяна и през фиксирани интервали, например веднъж на тримесечие.
Как помага PHP SOS конкретно?
- Отчитане на наличното: отчитаме всички слушащи услуги и проверяваме отвън кое е действително достъпно.
- Изграждане на набор от правила: изготвяме списък с разрешени за входящи и, където е смислено, изходящи връзки, съгласуван с Docker, Fail2ban и защитната стена на доставчика.
- Сигурен преход: прилагаме промените с втори път за достъп и план за отстъпление.
- Документиране: портове, разрешения и причини записваме в рамките на заздравяването на сървъра.
Често задавани въпроси
Какви портове се нуждае един уеб сървър?
За посетителите по правило само TCP 80 и 443, при HTTP/3 допълнително UDP 443. SSH и административните интерфейси трябва да са достъпни само от известни мрежи или през VPN. Портовете на бази данни, кеш и PHP-FPM не принадлежат в интернет.
Защитава ли защитната стена от PHP2Shell?
Не. PHP2Shell се експлоатира през уеб сървъра, тоест точно през портовете, които трябва да са отворени. Защитната стена обаче намалява останалата повърхност за атака, а ограничаването на изходящите връзки може да овладее последиците от пробив.
Защо ufw показва като затворени портове, които все пак са достъпни?
Известен случай са контейнерите на Docker: Docker записва публикуваните портове директно в правилата на пакетния филтър, в обход на ufw. Затова винаги проверявайте отворените портове допълнително отвън.
Достатъчна ли е защитната стена на моя хостинг доставчик?
Предходната защитна стена на доставчика е добър втори слой. Тя обаче не замества защитната стена на сървъра, защото при грешка в конфигурацията на доставчика или при вътрешни връзки не действа. Най-добре ползвайте и двете със същите основни правила.