# Prepoved izvajanja PHP v mapah za nalaganje

> Prepoved izvajanja PHP v nalaganjih: poiščite zapisljive mape, pravilno nastavite zapore v Nginx, Apache in PHP-FPM ter preizkusite z neškodljivo datoteko.

Vir: https://phpsos.de/sl/blog/php-in-uploads-verbieten/  
Stanje: 25.10.2026  
Ponudnik: PHP SOS, Nujna pomoč za PHP in varnost strežnikov. Takojšnja pomoč po WhatsAppu: https://wa.me/491736494914?text=Pozdravljeni%2C%20PHP%20SOS%2C%20potrebujemo%20pomo%C4%8D%20z%20na%C5%A1im%20stre%C5%BEnikom%20PHP.%20Podjetje%3A%20%E2%80%A6%20Spletna%20stran%3A%20%E2%80%A6

**Na kratko:** Za prepoved izvajanja PHP v nalaganjih najprej ugotovite vse mape, v katere sme PHP pisati, nato v vsaki od njih zaprite izvajanje skriptov v konfiguraciji strežnika, ne prek `.htaccess`. V Nginx mora zapora delovati pred splošnim blokom PHP, v Apache sodi v VirtualHost z `AllowOverride None`. `security.limit_extensions = .php` v poolu FPM in preizkus z neškodljivo datoteko delo zaključita.

## Zakaj prav zdaj štejejo mape za nalaganje

Po kritični ranljivosti, kot je PHP2Shell, napadalci le redko želijo kodo izvesti samo enkrat. Želijo se vrniti, tudi ko je strežnik pozneje popravljen. Najpreprostejša pot za to je webshell: majhna datoteka PHP, odložena v zapisljivi mapi in pozneje poklicana prek brskalnika.

Popravek take datoteke ne odstrani. Kdor je bil od 20. oktobra 2026 brez popravka na spletu, naj zato ne samo posodobi, ampak tudi preveri, ali kje ležijo nove datoteke PHP, in v prihodnje prepreči njihovo izvajanje. Kako razlagate najdbe, opisuje [Datoteke PHP v mapi za nalaganje: pravilna razlaga najdb](https://phpsos.de/sl/blog/php-dateien-im-upload-ordner/).

Zapora izvajanja se loti drugega pogoja. Ne prepreči, da bi bila datoteka odložena, prepreči pa, da bi jo strežnik obravnaval kot program.

## Najprej popis: kje sme PHP pisati?

Večina navodil navaja samo mapo za nalaganje. V praksi aplikacija PHP piše na veliko več mest. Vsako od njih, ki je dosegljivo prek spletnega strežnika, potrebuje zaporo.

| Vrsta mape | Tipična vsebina | Zakaj zapisljiva |
|---|---|---|
| Nalaganja, mediji | slike, PDF-ji, priponke | uporabniki in uredništvo nalagajo datoteke |
| Predpomnilnik | ustvarjene strani, sličice | aplikacija pospešuje dostavo |
| Začasne datoteke, uvoz, izvoz | datoteke CSV, vmesna stanja | vmesniki in izmenjava podatkov |
| Varnostne kopije v spletnem korenu | `.zip`, `.sql` | razširitve varnostno kopirajo »na kraju samem« |
| Dnevniki v spletnem korenu | dnevniki napak in odpravljanja napak | napačno konfigurirane aplikacije |

Katere mape so to na vašem strežniku, pokaže iskanje s pravicami izvajalnega uporabnika. Ničesar ne spremeni:

```bash
# Vse mape pod /var/www, v katere sme uporabnik PHP pisati
# (prilagodite uporabnika: www-data, nginx, apache ali lasten uporabnik poola)
sudo -u www-data find /var/www -type d -writable 2>/dev/null | sort
# Ali v teh mapah že ležijo datoteke PHP?
sudo -u www-data find /var/www -type d -writable -print0 2>/dev/null \
  | xargs -0 -I{} find {} -maxdepth 1 -type f -iname "*.ph*" 2>/dev/null
```

Če prvo iskanje vrne celoten spletni koren, programska koda pripada izvajalnemu uporabniku. To pred tem spremenite, kot opisuje vodnik [Pravice do datotek na spletnem strežniku](https://phpsos.de/sl/ratgeber/dateirechte-webserver/). Sicer ni mape, ki je ne bi bilo treba zapreti.

## Prepoved izvajanja PHP v nalaganjih: Nginx

Pri Nginx odloča vrstni red. Po dokumentaciji Nginx se regularni izrazi preverjajo v vrstnem redu konfiguracijske datoteke in prvi zadetek zmaga. Če splošni blok `location ~ \.php$` stoji pred vašo zaporo, se datoteka izvede. Preprosta predponska lokacija brez `^~` iz istega razloga ne ščiti.

Varen je regularni izraz za vse zaprte mape, ki stoji pred blokom PHP. Pokrije tudi redko uporabljane končnice:

```nginx
server {
    # ... osnovna konfiguracija ...

    # 1. Zapora za zapisljive mape, MORA stati pred blokom PHP
    location ~* ^/(uploads|media|cache|tmp|export)/.*\.(php\d?|phtml|phar|pht|phps)$ {
        deny all;
    }

    # 2. Splošni blok PHP: predaj samo obstoječe datoteke
    location ~ \.php$ {
        try_files $uri =404;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_pass unix:/run/php/seite1.sock;
    }
}
```

Imena map prilagodite svojemu popisu. Pri aplikacijah z eno samo vstopno točko je pot prek seznama dovoljenih še močnejša; vzorec prikazuje [Varna konfiguracija Nginx za PHP](https://phpsos.de/sl/blog/nginx-fuer-php-absichern/).

## Apache in PHP-FPM: zapora na pravem mestu

V Apache zapora sodi v konfiguracijo strežnika ali VirtualHost. `.htaccess` v mapi za nalaganje je neprimeren, ker ga lahko spremeni vsak, ki tam sme pisati. Za celoten spletni koren zato velja `AllowOverride None`. Po dokumentaciji Apache je `AllowOverride` dovoljen samo v blokih `<Directory>` brez regularnih izrazov, zato ga nastavite na spletnem korenu, zaporo pa prek `<DirectoryMatch>` pod njim:

```apache
<Directory /var/www/seite1/public>
    AllowOverride None
    Require all granted
</Directory>

<DirectoryMatch "^/var/www/seite1/public/(uploads|media|cache|tmp|export)/">
    <FilesMatch "\.(php\d?|phtml|phar|pht|phps)$">
        Require all denied
    </FilesMatch>
</DirectoryMatch>
```

Če še teče mod_php, v istem bloku dodajte `php_admin_flag engine off`. Pri PHP-FPM druga pregrada deluje v poolu: `security.limit_extensions` določa, katere končnice FPM sploh izvede. Po priročniku PHP je privzeto `.php .phar`; priporočena je omejitev na `.php`.

```ini
; v poolu, npr. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php
```

## Preizkus z neškodljivo datoteko

Ali zapora deluje, pokaže samo preizkus. V vsako zaprto mapo za kratek čas položite datoteko, ki izpiše le besedilo, jo pokličite prek domene in jo nato izbrišite.

1. Preverite konfiguracijo: `nginx -t` oziroma `apachectl configtest`, nato znova naložite.
2. Ustvarite preizkusno datoteko, na primer `uploads/sperrtest.php` z vsebino `<?php echo 'ausgefuehrt';`.
3. Pokličite jo z `curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/uploads/sperrtest.php`. Pričakovan je `403`.
4. Če se namesto tega pojavi `200` in besedilo »ausgefuehrt«, zapora ne deluje. Preverite konfiguracijo in vrstni red.
5. Preizkusno datoteko izbrišite in preizkus ponovite za vsako mapo in vsako spletno stran.

> **Pomembno:**
> Če pri popisu že najdete neznane datoteke PHP v zapisljivih mapah, ničesar ne brišite prenagljeno. Najprej zavarujte stanje in dnevnike, ker so te datoteke dokazno gradivo. Kako nato postopate, opisuje [Prepoznavanje in odstranjevanje webshellov](https://phpsos.de/sl/ratgeber/webshell-erkennen/).

Kako se funkcije nalaganja v kodi varno zgradijo, s preverjanjem tipa in hrambo zunaj spletnega korena, piše v vodniku [Varna izvedba nalaganja datotek v PHP](https://phpsos.de/sl/ratgeber/datei-upload-sicher/).

## Sklep: pisanje da, izvajanje ne

Zapora izvajanja v zapisljivih mapah je eden najučinkovitejših posameznih ukrepov proti trajnim stranskim vratom. Stane nekaj vrstic konfiguracije in pri čisto grajenih aplikacijah ničesar ne pokvari. Odločilne so tri stvari: popoln popis namesto samo očitne mape za nalaganje, pravilen vrstni red v Nginx oziroma prava raven v Apache in pravi preizkus z neškodljivo datoteko.

Z ločenimi lastniki za programsko kodo, `security.limit_extensions` v poolu in rednim iskanjem novih datotek PHP nastane stanje, v katerem je odložena datoteka sicer nadležna, a komaj še uporabna. To ne nadomešča posodobitve ne iskanja sledi, napadalcem pa vzame eno njihovih najpomembnejših orodij.

## Kako PHP SOS pomaga konkretno?

- **Popis:** ugotovimo vse zapisljive mape za vsako spletno stran in jih preverimo za obstoječe datoteke PHP.
- **Nastavitev zapor:** zapore izvajanja vnesemo v Nginx, Apache in PHP-FPM, v pravem vrstnem redu in brez odvisnosti od `.htaccess`.
- **Preizkus:** vsako zaporo preverimo z neškodljivo datoteko in rezultat dokumentiramo.
- **Čiščenje:** pri najdbah prevzamemo zavarovanje sledi in [odstranjevanje zlonamerne kode](https://phpsos.de/sl/leistungen/malware-entfernung/).


## Pogosta vprašanja

**Ali za prepoved PHP zadošča .htaccess v mapi za nalaganje?**  
Ne zanesljivo. Kdor lahko piše v mapo, lahko spremeni ali zamenja tudi .htaccess. Zapora sodi v konfiguracijo strežnika ali VirtualHost, za spletni koren pa naj velja AllowOverride None.

**Katere mape moram zapreti?**  
Vse mape, v katere sme pisati uporabnik PHP in so dosegljive prek spletnega strežnika. Poleg map za nalaganje so to pogosto imeniki za predpomnilnik, začasne datoteke, uvoz, izvoz in varnostne kopije. Ugotovite jih z iskanjem kot izvajalni uporabnik.

**Ali zapora pokvari mojo spletno stran?**  
Praviloma ne, ker v mapah za nalaganje in predpomnilnik pri čistih aplikacijah ni skriptov, ki bi se klicali neposredno. Posamezne razširitve tam vendarle odlagajo datoteke PHP. Zato po prehodu preizkusite najpomembnejše funkcije.

**Ali zapora ščiti pred PHP2Shell?**  
Ne. PHP2Shell je ranljivost v interpreterju PHP, ki se zapre samo s posodobitvijo in ponovnim zagonom vseh delavcev. Zapora pa prepreči, da bi se odložena datoteka pozneje poklicala prek brskalnika in uporabila kot trajna stranska vrata.

## Viri

- [Nginx: izbira blokov location](https://nginx.org/en/docs/http/ngx_http_core_module.html)
- [Apache: jedrne funkcije (AllowOverride, FilesMatch)](https://httpd.apache.org/docs/2.4/mod/core.html)
- [Priročnik PHP: konfiguracija FPM (security.limit_extensions)](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheets (File Upload)](https://cheatsheetseries.owasp.org/)
