# Jak zabránit Local File Inclusion v PHP: LFI a RFI

> Jak zabránit Local File Inclusion v PHP: seznamy povolených hodnot místo cest z požadavku, kontrola realpath, vypnuté allow_url_include a open_basedir.

Zdroj: https://phpsos.de/cs/ratgeber/lfi-rfi/  
Stav: 25.10.2026  
Poskytovatel: PHP SOS, Nouzová pomoc pro PHP a bezpečnost serverů. Okamžitá pomoc přes WhatsApp: https://wa.me/491736494914?text=Dobr%C3%BD%20den%2C%20PHP%20SOS%2C%20pot%C5%99ebujeme%20pomoc%20s%20na%C5%A1%C3%ADm%20PHP%20serverem.%20Firma%3A%20%E2%80%A6%20Web%3A%20%E2%80%A6

**Stručně:** Local File Inclusion v PHP vzniká, když `include`, `require` nebo souborové funkce používají cestu, kterou může návštěvník ovlivnit. Bezpečným řešením je pevný seznam povolených hodnot: parametr vybírá jen ze známých hodnot, samotná cesta nikdy nepochází z požadavku. Na straně serveru ochranu doplňují `allow_url_include=Off`, `open_basedir` a restriktivní práva k souborům.

## Co je Local File Inclusion v PHP?

Mnoho starších aplikací PHP načítá stránky podle jednoduchého vzoru: parametr v adrese určuje, který soubor se vloží. Pokud je tento parametr bez kontroly předán do `include`, může útočník vyžádat jiné soubory na serveru. V mírnějším případě přečte konfigurační soubory s přístupovými údaji. V horším případě přiměje PHP spustit obsah, který předtím uložil jinde, například v uploadu nebo v logu.

Pro vedení je důležité: tato zranitelnost sídlí zřídka v jádru CMS, většinou ve vlastních vývojích, starých rozšířeních nebo narychlo postavených doplňkových stránkách. V běžném provozu se neprojeví, protože web funguje normálně.

U **Remote File Inclusion** načítá PHP kód z cizí adresy. To předpokládá, že je zapnuto `allow_url_include`. V aktuálních verzích PHP je toto nastavení ve výchozím stavu vypnuto, na starých nebo špatně nakonfigurovaných serverech se ale stále najde.

### Typické zranitelné vzory v kódu

Ohrožená jsou místa, kde se hodnota z požadavku, z cookies nebo z databáze stane součástí cesty k souboru. Častými příklady jsou přepínače jazyka, které načítají jazykový soubor podle parametru, výběr stránek přes parametr v adrese, výběr šablony nebo motivu a skripty pro stahování dokumentů. Připojené `.php` na konci cesty přitom spolehlivě nechrání, protože útočníci mohou sáhnout po existujících souborech PHP.

První přehled získáte hledáním vložení, která obsahují proměnné. Najde i neškodné nálezy, ale ukáže, kam se musí podívat podrobněji:

```bash
grep -rnE --include='*.php' '(include|require)(_once)?\s*\(?\s*\$' /var/www/projekt \
  | grep -v '/vendor/'
```

U každého nálezu zkontrolujte, odkud proměnná pochází a zda ji může návštěvník ovlivnit.

## Bezpečný vzor: seznam povolených místo cesty z požadavku

Nejspolehlivější ochranou je, že návštěvník vybírá jen klíč a aplikace sama rozhoduje, který soubor k němu patří. Cesta se nikdy neskládá ze vstupů.

```php
<?php
declare(strict_types=1);

$seiten = [
    'start'      => __DIR__ . '/../vorlagen/start.php',
    'leistungen' => __DIR__ . '/../vorlagen/leistungen.php',
    'kontakt'    => __DIR__ . '/../vorlagen/kontakt.php',
];

$wahl = is_string($_GET['seite'] ?? null) ? $_GET['seite'] : 'start';

if (!array_key_exists($wahl, $seiten)) {
    http_response_code(404);
    $wahl = 'start';
}

require $seiten[$wahl];
```

Tento vzor je krátký, snadno se kontroluje a nenechává žádný prostor. V moderních frameworcích tuto úlohu přebírá router; i tam by se kontrolery a šablony nikdy neměly určovat z volných parametrů.

## Když musí být názvy souborů dynamické: realpath a základní adresář

Někdy není pevný seznam praktický, například u stahování z adresáře dokumentů. Pak zkontrolujte vyřešenou cestu proti pevnému základnímu adresáři a povolte jen jednoduché názvy souborů.

```php
<?php
declare(strict_types=1);

const BASIS = '/var/www/daten/dokumente';

function sichererPfad(string $name): string
{
    if (!preg_match('/^[a-z0-9_-]{1,64}\.pdf$/', $name)) {
        throw new InvalidArgumentException('Ungültiger Dateiname.');
    }

    $basis = realpath(BASIS);
    $pfad  = realpath($basis . DIRECTORY_SEPARATOR . $name);

    if ($basis === false || $pfad === false
        || !str_starts_with($pfad, $basis . DIRECTORY_SEPARATOR)) {
        throw new RuntimeException('Datei nicht gefunden.');
    }

    return $pfad;
}

// Doručit místo vložit: readfile() nespouští žádný kód.
$datei = sichererPfad((string) ($_GET['datei'] ?? ''));
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . basename($datei) . '"');
readfile($datei);
```

Rozhodující jsou dva body. Regulární výraz nepovoluje oddělovače adresářů ani speciální znaky. A soubory, které se mají jen doručit, se čtou pomocí `readfile()`, nikoli `include`, aby se obsažený kód nikdy nespustil. `str_starts_with()` je k dispozici od PHP 8.0.

## Zabezpečení na straně serveru: php.ini, open_basedir a práva

Bezpečný kód je první linie obrany. Konfigurace serveru zajistí, že přehlédnutá chyba způsobí menší škodu.

```ini
; php.ini nebo pool FPM (php_admin_value / php_admin_flag)
allow_url_include = Off
allow_url_fopen   = Off   ; jen pokud aplikace nenačítá externí adresy URL
open_basedir      = /var/www/kunde-a/:/tmp/kunde-a/
display_errors    = Off
log_errors        = On
```

`allow_url_fopen` potřebují některé aplikace pro aktualizace nebo rozhraní. Vypnutí nejdřív vyzkoušejte v testovacím prostředí. `open_basedir` omezuje PHP na vlastní adresáře webu, což je zvlášť důležité na serverech s více zákazníky. Podrobnosti najdete v příručkách [open_basedir a izolace jednotlivých webů](https://phpsos.de/cs/ratgeber/open-basedir/) a [Jak bezpečně nakonfigurovat php.ini](https://phpsos.de/cs/ratgeber/php-ini-sicher-konfigurieren/).

Doplňkově by složky pro uploady a cache neměly být spustitelné a logy neměly ležet ve webrootu. Útočníkovi tak chybí místa, kam by mohl uložit vlastní obsah pro vložení.

## Podle čeho poznáte pokusy o zneužití v lozích

Pokusy o zneužití file inclusion zanechávají většinou stopy v access logu. Nápadné jsou parametry, které obsahují změny adresáře, absolutní systémové cesty, obálky streamů nebo úplné cizí adresy, zejména u parametrů jako `page`, `file`, `template` nebo `lang`. Jednotlivý nález je většinou automatizované skenování. Důležité je, zda byl takový požadavek zodpovězen stavovým kódem 200 a neobvyklou velikostí odpovědi.

Jak postupovat systematicky, popisuje příručka [Vyhodnocení logů webového serveru po útoku](https://phpsos.de/cs/ratgeber/logfiles-auswerten/). Pokud najdete náznaky úspěšných přístupů, vycházejte z toho, že konfigurační soubory byly přečteny, a obnovte v nich obsažené přístupové údaje.

Patří sem především hesla k databázi, klíče platebních a přepravních služeb, přístupy SMTP a tajné klíče CMS. Zkontrolujte také, zda od prvního podezřelého přístupu nevznikly nové soubory. Kdo mohl soubory číst, mohl se také pokusit uložit vlastní. V takovém případě už nejde jen o chybu v kódu, ale o bezpečnostní incident, který by se měl zpracovat strukturovaně.

### Trvalé zajištění oprav

Po opravě se vyplatí ochrannou funkci zajistit automatizovanými testy. Jednoduchý test předá výběru stránek neznámé hodnoty, změny adresáře a prázdné vstupy a ve všech případech očekává výchozí stránku nebo chybu 404. Takové testy zabrání tomu, aby pozdější změna zranitelnost nepozorovaně znovu otevřela.

Pro vedení a projektové vedoucí je užitečná otázka na agenturu: Existují v aplikaci místa, kde se soubory načítají podle parametrů, a jak jsou zabezpečena? Konkrétní odpověď s uvedením míst a testů je dobré znamení.

## Jak konkrétně pomáhá PHP SOS?

- V kódu cíleně hledáme `include`, `require` a souborové funkce se vstupy z požadavku a každý nález hodnotíme.
- Nastavujeme `allow_url_include`, `open_basedir` a vhodná práva pro každý web a ověřujeme, že aplikace poté dál funguje.
- Vyhodnocujeme logy kvůli pokusům o zneužití a ujasňujeme, zda je nutné obnovit přístupové údaje.
- Pro nezávislou kontrolu nabízíme pro aplikace PHP [bezpečnostní audit s penetračním testem](https://phpsos.de/cs/leistungen/sicherheitsaudit-penetrationstest/).


## Časté dotazy

**Jaký je rozdíl mezi LFI a RFI?**  
U Local File Inclusion aplikace načte soubor, který už na serveru leží, ale neměl by se načítat. U Remote File Inclusion se kód stahuje z cizí adresy. RFI vyžaduje nebezpečné nastavení PHP, LFI nikoli.

**Stačí z parametru odstranit ../?**  
Ne. Takové filtry lze často obejít kódováním nebo variantami a přehlížejí absolutní cesty. Bezpečné je jen pevné přiřazení povolených hodnot k souborům nebo kontrola vyřešené cesty proti základnímu adresáři.

**Chrání open_basedir před file inclusion úplně?**  
Ne, omezuje jen adresáře, k nimž smí PHP přistupovat. V rámci těchto hranic zůstává zranitelnost zneužitelná. open_basedir je dodatečná ochranná vrstva a bezpečný kód nenahrazuje.

**Týká se LFI i CMS jako WordPress?**  
Jádro běžných CMS se v tomto bodě považuje za dobře prověřené. Zranitelnosti se objevují spíše v rozšířeních, šablonách a vlastních vývojích. Aktualizace rozšíření a pohled do jejich bezpečnostních hlášení jsou proto důležité.

## Zdroje

- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [BSI IT-Grundschutz](https://www.bsi.bund.de/DE/Themen/Unternehmen-und-Organisationen/Standards-und-Zertifizierung/IT-Grundschutz/it-grundschutz_node.html)
