Aktuální situace: PHP2Shell. Dvě kritické zranitelnosti PHP (CVSS 9.2) umožňují spustit škodlivý kód bez přihlášení. Záplaty od 21. 10. 2026.Co nyní udělat →

Jak zabránit Local File Inclusion v PHP: LFI a RFI

Pokud include nebo require přebírají cesty z požadavku, mohou útočníci načítat cizí soubory. Jak bezpečně postavit výběr stránek a šablon a server navíc zabezpečit.

Stav 5 min čteníPoradna

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:

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
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
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.

; 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ů a Jak bezpečně nakonfigurovat php.ini.

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. 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.

Č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

Okamžitý kontakt

Každá hodina se počítá. Napište nám hned.

Stručně popište systém, verzi PHP a pozorování. Ozveme se, vyjasníme situaci a budeme pracovat na dálku, dokud nebude incident uzavřen a zdokumentován.

Okamžitá pomoc