Trumpai: žiniatinklio serverio naudotojas, kuriuo veikia PHP, pagal mažiausių teisių principą (Least Privilege) turėtų skaityti tik tai, ko reikia svetainei, ir rašyti tik ten, kur ji saugo duomenis. Jis neturi prisijungimo shell, sudo teisių, savų cron užduočių ir SSH raktų, o jo duomenų bazės paskyra gali tik skaityti ir keisti duomenis. systemd parinktys, tokios kaip ProtectSystem ir PrivateTmp, papildomai apriboja PHP-FPM.
Klausimas po įsilaužimo: ką šis procesas gali daryti?
PHP2Shell atveju kalbama apie kenkėjiško kodo vykdymą nuotoliu be prisijungimo. Jei tai pavyksta, kodas veikia ne tuštumoje, o kaip konkretus naudotojas: PHP proceso naudotojas. Viską, ką šis naudotojas gali, nuo to momento gali ir užpuolikas.
Daugelyje serverių to stebėtinai daug. Naudotojas valdo visą programos kodą, turi shell, gali kurti savus cron užduotis, o programos duomenų bazės paskyra turi visas teises į visas duomenų bazes. Užpuolikui tai idealus pagrindas įsitvirtinti ilgam.
Kaip teisingai paskirstyti failus ir aplankus, aprašo vadovas Failų teisės žiniatinklio serveryje. Šis straipsnis nagrinėja pačią paskyrą: prisijungimą, ypatingas teises, suplanuotas užduotis, duomenų bazę ir ribas, kurias nubrėžia systemd.
Inventorizacija: kas yra žiniatinklio serverio naudotojas?
Pirmiausia išsiaiškinkite, kurie naudotojai apskritai dalyvauja. Su PHP-FPM kiekvienas baseinas per user ir group nustato, kokiu naudotoju veikia jo darbiniai procesai. Su mod_php PHP veikia kaip Apache naudotojas. Kaip PHP-FPM pagrindinis procesas ir darbiniai procesai veikia kartu, aiškina PHP-FPM paaiškinimas: procesų valdytojas, baseinai ir darbiniai procesai. Šios komandos tik skaito:
# Kokiais naudotojais veikia žiniatinklio serveris ir PHP?
ps -eo user,group,cmd | grep -E "[p]hp-fpm|[n]ginx|[a]pache2|[h]ttpd" | sort | uniq -c
# Paskyros duomenys: namų katalogas ir shell (pritaikyti naudotoją)
getent passwd www-data
# Ar naudotojas turi sudo teisių?
sudo -l -U www-data
# Ar turi savų cron užduočių?
sudo crontab -l -u www-data
# SSH raktai namų kataloge (Debian/Ubuntu sistemose tai /var/www)
sudo ls -la /var/www/.ssh 2>/dev/null
Užsirašykite rezultatus kiekvienam baseino naudotojui. Jei visos svetainės veikia tuo pačiu naudotoju, tai pirmas punktas sąraše; kaip tai atskirti, aprašo PHP-FPM baseinų izoliavimas kiekvienai svetainei.
Žiniatinklio serverio naudotojo teisės: ko paskyrai nereikia
Toliau pateikta apžvalga rodo, ko žiniatinklio serverio naudotojui paprastai nereikia ir kaip tai atimti.
| Teisė | Kodėl pavojinga | Rekomendacija |
|---|---|---|
| Prisijungimo shell | galimas prisijungimas ir interaktyvus naudojimas | shell /usr/sbin/nologin |
| sudo įrašai | kelias į root teises | jokių įrašų, net atskiroms komandoms |
| Sava crontab | nuolatinės užpakalinės durys, išgyvenančios kiekvieną paleidimą iš naujo | jokios, apriboti per /etc/cron.allow |
| SSH raktai | prisijungimas iš išorės | jokių authorized_keys |
| Programos kodo savininkas | kodą galima pakeisti | savininkas yra diegimo (deploy) naudotojas |
| Rašymo teisės už duomenų aplankų ribų | kenkėjiško kodo padėjimas | tik įkėlimai, talpykla, temp |
| Visos duomenų bazės teisės | kitų programų duomenų bazės, prieiga prie failų per duomenų bazę | tik sava duomenų bazė, tik duomenų teisės |
Shell nustatote komanda usermod -s /usr/sbin/nologin www-data. Cron taisyklė tokia: jei yra /etc/cron.allow, savų cron užduočių kurti gali tik ten išvardyti naudotojai. Naujai sukurtos cron užduotys ar nežinomi SSH raktai tipiškai rodo, kad kažkas įsitvirtino. Ypač aiškų perspėjimo ženklą aprašo straipsnis Perspėjimo ženklas: žiniatinklio serveris paleidžia shell.
Duomenų bazė: teises riboti iki būtinų
Svetainės duomenų bazės paskyra faktiškai yra antras žiniatinklio serverio naudotojas. Jos prisijungimo duomenys yra programos konfigūracijos faile ir įterptam kodui prieinami skaityti. Todėl ji turėtų galėti tik tai, ko programai reikia veikiant.
-- Pavyzdys MySQL/MariaDB: sava paskyra kiekvienai svetainei, tik duomenų teisės
CREATE USER 'seite1'@'localhost' IDENTIFIED BY 'hier-ein-langes-Zufallspasswort';
GRANT SELECT, INSERT, UPDATE, DELETE ON seite1.* TO 'seite1'@'localhost';
-- Patikrinti teises
SHOW GRANTS FOR 'seite1'@'localhost';
Teisių FILE, SUPER ar GRANT OPTION ir prieigos prie *.* programos paskyrai nereikia. Daugeliui CMS atnaujinimams papildomai reikia CREATE, ALTER, INDEX ir DROP. Tvarkingiau turėti atskirą paskyrą atnaujinimams ir migracijoms, naudojamą tik techninės priežiūros metu. Reikalavimus patikrinkite gamintojo dokumentacijoje.
PHP-FPM papildomai apriboti su systemd
systemd gali apriboti tarnybos požiūrį į sistemą. PHP-FPM ypač tinka kelios parinktys iš systemd dokumentacijos. Jos veikia ir pagrindinį procesą, kuris paleidžiamas kaip root.
# sudo systemctl edit php8.3-fpm
[Service]
# /usr, /boot ir /etc tik skaitymui
ProtectSystem=full
# /home, /root ir /run/user nematomi
ProtectHome=true
# savas /tmp ir /var/tmp tik šiai tarnybai
PrivateTmp=true
# jokio teisių išplėtimo per setuid ar failų capabilities
NoNewPrivileges=true
Išbandykite šias parinktis prieš naudodami gamyboje. Jei svetainės yra aplanke /home, ProtectHome=true užblokuos prieigą. NoNewPrivileges=true gali trikdyti programas, kurias iškviečia PHP ir kurios paleidžiamos keičia savo grupės teises, pavyzdžiui, kai kurias vietines pašto programas PHP funkcijai mail(). Todėl eikite žingsniais:
- Pridėkite vieną parinktį, paleiskite tarnybą iš naujo, patikrinkite
systemctl status php8.3-fpm. - Išbandykite pagrindines svetainės funkcijas: prisijungimą, formas, laiškų siuntimą, įkėlimus, programos cron užduotis.
- Peržiūrėkite PHP-FPM ir programos klaidų žurnalą.
- Tik tada pridėkite kitą parinktį.
Galiausiai lizdas (socket): pagal PHP vadovą jam pagal numatytuosius nustatymus suteikiamos teisės 0660 baseino naudotojui ir grupei. Nustatykite listen.owner ir listen.group taip, kad prisijungti galėtų tik žiniatinklio serveris, o ne kiekvienas vietinis naudotojas.
Minimalios teisės yra požiūris, o ne vienkartinis projektas. Šio straipsnio inventorizaciją kartokite po kiekvieno pertvarkymo ir kiekvienos naujos svetainės, nes naujos tarnybos dažnai ateina su dosniomis numatytosiomis teisėmis.
Išvada: užpuolikas paveldi tai, ką leidžiate
Least Privilege skamba kaip teorija, bet tai labai praktiškas klausimas: ką gali daryti tas, kas vykdo kodą kaip žiniatinklio serverio naudotojas? Serveryje su shell, sudo įrašu, sava crontab ir duomenų bazės paskyra su visomis teisėmis atsakymas toks: beveik viską. Apribotame serveryje dažniau lieka incidentas vienoje svetainėje, kurį galima aptikti ir sutvarkyti.
Perėjimui nereikia naujos programinės įrangos, tik kruopščios inventorizacijos ir nuoseklių bandymų. Pradėkite nuo punktų, kurie vargu ar ką nors sulaužys, t. y. shell, sudo ir SSH raktų, o tada pereikite prie duomenų bazės teisių ir systemd parinkčių.
Kaip PHP SOS padeda konkrečiai?
- Patikrinti paskyras: kiekvienam baseinui ir žiniatinklio serveriui surenkame naudotoją, shell, sudo įrašus, cron užduotis, SSH raktus ir duomenų bazės teises.
- Sumažinti teises: atimame tai, ko nereikia, ir atskiriame diegimo, vykdymo ir techninės priežiūros paskyras.
- Apriboti tarnybas: systemd apsaugos parinktis nustatome žingsniais ir po kiekvieno žingsnio išbandome svetainę.
- Išlaikyti ilgam: serverio sutvirtinimo rėmuose dokumentuojame teisių koncepciją būsimiems pakeitimams.
Dažni klausimai
Kuriuo naudotoju veikia PHP mano serveryje?
Tai priklauso nuo veikimo režimo. Su mod_php PHP veikia kaip Apache naudotojas, pavyzdžiui, www-data arba apache. Su PHP-FPM kiekvienas baseinas (pool) naudotoją nustato per user ir group; pagrindinis procesas dažniausiai veikia kaip root, darbiniai procesai (worker) kaip baseino naudotojas. Komanda ps tai parodo greitai.
Ar žiniatinklio serverio naudotojui reikia prisijungimo shell?
Ne. Nei Nginx, nei Apache, nei PHP-FPM šiam naudotojui interaktyvaus prisijungimo nereikalauja. Shell, pavyzdžiui, /usr/sbin/nologin, neleidžia niekam prisijungti su šia paskyra. Diegimas (deployment) vyksta per atskirą naudotoją.
Kodėl www-data neturėtų turėti sudo teisių, net vienai komandai?
Nes įterptas kodas paveldi būtent šias teises. Net iš pažiūros nekenksminga komanda, jei ji netinkamai leidžiama, gali baigtis root teisėmis. Užduotys, kurioms reikia didesnių teisių, turi būti atskirose tarnybose ar procesuose už žiniatinklio serverio ribų.
Ar tai padeda nuo PHP2Shell?
Pažeidžiamumo tai neuždaro, tam reikia PHP atnaujinimo ir visų darbinių procesų paleidimo iš naujo. Tačiau minimalios teisės apriboja, ką užpuolikas gali daryti po sėkmingo išnaudojimo: keisti failus, įsitvirtinti, pasiekti kitas svetaines.