# Zabraniti izvršavanje PHP-a u mapama za prijenose

> Zabranite izvršavanje PHP-a u prijenosima: pronađite zapisive mape, ispravno postavite blokade u Nginxu, Apacheu i PHP-FPM-u te testirajte bezazlenom datotekom.

Izvor: https://phpsos.de/hr/blog/php-in-uploads-verbieten/  
Stanje: 25.10.2026  
Pružatelj: PHP SOS, PHP hitna pomoć i sigurnost poslužitelja. Hitna pomoć putem WhatsAppa: https://wa.me/491736494914?text=Pozdrav%20PHP%20SOS%2C%20trebamo%20pomo%C4%87%20s%20na%C5%A1im%20PHP%20poslu%C5%BEiteljem.%20Tvrtka%3A%20%E2%80%A6%20Web-stranica%3A%20%E2%80%A6

**Ukratko:** Da biste zabranili izvršavanje PHP-a u prijenosima, najprije utvrdite sve mape u koje PHP smije pisati, a zatim u svakoj od njih blokirajte izvršavanje skripti u konfiguraciji poslužitelja, a ne putem `.htaccess`. U Nginxu blokada mora djelovati prije općeg PHP bloka, u Apacheu pripada u VirtualHost s `AllowOverride None`. `security.limit_extensions = .php` u FPM poolu i test bezazlenom datotekom zaokružuju posao.

## Zašto upravo sada mape za prijenose računaju

Nakon kritične ranjivosti poput PHP2Shella napadači rijetko žele izvršiti kod samo jednom. Žele se vratiti, čak i kad se poslužitelj kasnije zakrpa. Najjednostavniji put za to je web shell: mala PHP datoteka odložena u zapisivu mapu i kasnije pozvana preko preglednika.

Zakrpa takvu datoteku ne uklanja. Tko je od 20. listopada 2026. bio na mreži nezakrpan, trebao bi stoga ne samo ažurirati nego i provjeriti leži li negdje nova PHP datoteka te ubuduće spriječiti njezino izvršavanje. Kako tumačiti nalaze, opisuje članak [PHP datoteke u mapi za prijenose: ispravno tumačiti nalaze](https://phpsos.de/hr/blog/php-dateien-im-upload-ordner/).

Blokada izvršavanja djeluje na drugom uvjetu. Ne sprječava odlaganje datoteke, ali sprječava da je poslužitelj tretira kao program.

## Najprije inventura: gdje PHP smije pisati?

Većina uputa spominje samo mapu za prijenose. U praksi PHP aplikacija piše na mnogo više mjesta. Svako od njih koje je dostupno preko web-poslužitelja treba blokadu.

| Vrsta mape | Tipičan sadržaj | Zašto je zapisiva |
|---|---|---|
| Prijenosi, mediji | slike, PDF-ovi, privici | korisnici i redakcija učitavaju datoteke |
| Predmemorija | generirane stranice, sličice | aplikacija ubrzava isporuku |
| Privremene, uvoz, izvoz | CSV datoteke, međustanja | sučelja i razmjena podataka |
| Sigurnosne kopije u web-korijenu | `.zip`, `.sql` | proširenja spremaju „na licu mjesta“ |
| Zapisnici u web-korijenu | zapisnici pogrešaka i debugiranja | pogrešno konfigurirane aplikacije |

Koje su to mape na Vašem poslužitelju, pokazuje pretraga s pravima korisnika pod kojim aplikacija radi. Ništa ne mijenja:

```bash
# Sve mape pod /var/www u koje PHP korisnik smije pisati
# (prilagoditi korisnika: www-data, nginx, apache ili vlastiti korisnik poola)
sudo -u www-data find /var/www -type d -writable 2>/dev/null | sort
# Leže li u tim mapama već PHP datoteke?
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
```

Ako prva pretraga vrati cijeli web-korijen, programski kod pripada korisniku pod kojim aplikacija radi. To biste trebali prethodno promijeniti, kako opisuje vodič [Prava datoteka na web-poslužitelju](https://phpsos.de/hr/ratgeber/dateirechte-webserver/). Inače ne postoji mapa koju ne bi trebalo blokirati.

## Zabraniti izvršavanje PHP-a u prijenosima: Nginx

Kod Nginxa odlučuje redoslijed. Prema dokumentaciji Nginxa regularni izrazi provjeravaju se redoslijedom iz konfiguracijske datoteke i prvi pogodak pobjeđuje. Ako opći blok `location ~ \.php$` stoji prije Vaše blokade, datoteka se izvršava. Jednostavna prefiksna lokacija bez `^~` iz istog razloga ne štiti.

Sigurno je regularni izraz za sve blokirane mape koji stoji ispred PHP bloka. Pokriva i rijetko korištene ekstenzije:

```nginx
server {
    # ... osnovna konfiguracija ...

    # 1. Blokada za zapisive mape, MORA stajati prije PHP bloka
    location ~* ^/(uploads|media|cache|tmp|export)/.*\.(php\d?|phtml|phar|pht|phps)$ {
        deny all;
    }

    # 2. Opći PHP blok: predavati samo postojeć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;
    }
}
```

Nazive mapa prilagodite svojoj inventuri. Kod aplikacija s jednom jedinom ulaznom točkom put preko popisa dopuštenih još je jači; obrazac pokazuje članak [Sigurno konfigurirati Nginx za PHP](https://phpsos.de/hr/blog/nginx-fuer-php-absichern/).

## Apache i PHP-FPM: blokada na pravom mjestu

U Apacheu blokada pripada u konfiguraciju poslužitelja ili VirtualHost. `.htaccess` u mapi za prijenose je neprikladan jer ga može promijeniti svatko tko tamo smije pisati. Za cijeli web-korijen stoga vrijedi `AllowOverride None`. Prema dokumentaciji Apachea `AllowOverride` je dopušten samo u blokovima `<Directory>` bez regularnih izraza, pa ga postavite na web-korijen, a blokadu pomoću `<DirectoryMatch>` ispod njega:

```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>
```

Ako još radi mod_php, u istom bloku dodajte `php_admin_flag engine off`. Kod PHP-FPM-a druga prepreka djeluje u poolu: `security.limit_extensions` određuje koje ekstenzije FPM uopće izvršava. Prema PHP priručniku zadano je `.php .phar`; preporučuje se ograničenje na `.php`.

```ini
; u poolu, npr. /etc/php/8.3/fpm/pool.d/seite1.conf
security.limit_extensions = .php
```

## Testirati bezazlenom datotekom

Djeluje li blokada, pokazuje samo test. U svaku blokiranu mapu nakratko odložite datoteku koja samo ispisuje tekst, pozovite je preko domene i zatim je izbrišite.

1. Provjeriti konfiguraciju: `nginx -t` odnosno `apachectl configtest`, zatim ponovno učitati.
2. Izraditi testnu datoteku, primjerice `uploads/sperrtest.php` sa sadržajem `<?php echo 'ausgefuehrt';`.
3. Pozvati je naredbom `curl -s -o /dev/null -w "%{http_code}\n" https://www.example.de/uploads/sperrtest.php`. Očekuje se `403`.
4. Ako se umjesto toga pojavi `200` i tekst „ausgefuehrt“, blokada ne djeluje. Provjeriti konfiguraciju i redoslijed.
5. Izbrisati testnu datoteku i test ponoviti za svaku mapu i svaku web-stranicu.

> **Važno:**
> Ako pri inventuri već pronađete nepoznate PHP datoteke u zapisivim mapama, ne brišite ništa prenagljeno. Najprije osigurajte stanje i zapisnike, jer su te datoteke dokazni predmeti. Kako zatim postupiti, opisuje članak [Prepoznati i ukloniti web shellove](https://phpsos.de/hr/ratgeber/webshell-erkennen/).

Kako se funkcije prijenosa u kodu grade sigurno, uz provjeru tipa i pohranu izvan web-korijena, piše u vodiču [Sigurno implementirati prijenose datoteka u PHP-u](https://phpsos.de/hr/ratgeber/datei-upload-sicher/).

## Zaključak: pisati da, izvršavati ne

Blokada izvršavanja u zapisivim mapama jedna je od najdjelotvornijih pojedinačnih mjera protiv trajnih stražnjih vrata. Košta nekoliko redaka konfiguracije i kod uredno izgrađenih aplikacija ništa ne kvari. Odlučujuće su tri stvari: potpuna inventura umjesto samo očite mape za prijenose, ispravan redoslijed u Nginxu odnosno ispravna razina u Apacheu i pravi test bezazlenom datotekom.

Dopunjeno odvojenim vlasnicima programskog koda, `security.limit_extensions` u poolu i redovitom potragom za novim PHP datotekama, nastaje stanje u kojem je odložena datoteka doduše neugodna, ali jedva upotrebljiva. To ne zamjenjuje ni ažuriranje ni potragu za tragovima, ali napadačima oduzima jedan od njihovih najvažnijih alata.

## Kako PHP SOS konkretno pomaže?

- **Inventura:** utvrđujemo sve zapisive mape po web-stranici i provjeravamo postoje li u njima PHP datoteke.
- **Postaviti blokade:** upisujemo blokade izvršavanja u Nginx, Apache i PHP-FPM, pravilnim redoslijedom i bez ovisnosti o `.htaccess`.
- **Testirati:** svaku blokadu provjeravamo bezazlenom datotekom i dokumentiramo rezultat.
- **Očistiti:** kod nalaza preuzimamo osiguranje tragova i [uklanjanje zlonamjernog softvera](https://phpsos.de/hr/leistungen/malware-entfernung/).


## Česta pitanja

**Je li .htaccess u mapi za prijenose dovoljna da se zabrani PHP?**  
Nije pouzdano. Tko može pisati u mapu, može promijeniti ili zamijeniti i .htaccess. Blokada pripada u konfiguraciju poslužitelja ili VirtualHost, a za web-korijen treba vrijediti AllowOverride None.

**Koje mape moram blokirati?**  
Sve mape u koje PHP korisnik smije pisati i koje su dostupne preko web-poslužitelja. Osim mapa za prijenose to su često direktoriji za predmemoriju, privremene datoteke, uvoz, izvoz i sigurnosne kopije. Pronađite ih pretragom kao korisnik pod kojim aplikacija radi.

**Hoće li blokada pokvariti moju web-stranicu?**  
U pravilu neće, jer se u mapama za prijenose i predmemoriju kod čistih aplikacija ne nalaze skripte koje se izravno pozivaju. Pojedina proširenja ipak tamo odlažu PHP datoteke. Zato nakon prelaska testirajte najvažnije funkcije.

**Štiti li blokada od PHP2Shella?**  
Ne. PHP2Shell je ranjivost u PHP interpreteru koja se zatvara samo ažuriranjem i ponovnim pokretanjem svih workera. Blokada ipak sprječava da se odložena datoteka kasnije pozove preko preglednika i iskoristi kao trajna stražnja vrata.

## Izvori

- [Nginx: odabir location blokova](https://nginx.org/en/docs/http/ngx_http_core_module.html)
- [Apache: temeljne funkcije (AllowOverride, FilesMatch)](https://httpd.apache.org/docs/2.4/mod/core.html)
- [PHP priručnik: FPM konfiguracija (security.limit_extensions)](https://www.php.net/manual/en/install.fpm.configuration.php)
- [OWASP Cheat Sheets (File Upload)](https://cheatsheetseries.owasp.org/)
