Dabartinė padėtis: PHP2Shell. Du kritiniai PHP pažeidžiamumai (CVSS 9,2) leidžia vykdyti kenkėjišką kodą be prisijungimo. Pataisos nuo 2026-10-21.Ką dabar daryti →

Kaip užkirsti kelią Local File Inclusion PHP: LFI ir RFI

Kai include ar require perima kelius iš užklausos, užpuolikai gali įkelti svetimus failus. Kaip saugiai sukurti puslapių pasirinkimą ir šablonus bei papildomai apsaugoti serverį.

Būsena 5 min. skaitymoVadovai

Trumpai: Local File Inclusion PHP atsiranda, kai include, require ar failų funkcijos naudoja kelią, kurį lankytojas gali paveikti. Saugus sprendimas yra fiksuotas leistinų sąrašas: parametras renkasi tik iš žinomų reikšmių, pats kelias niekada neateina iš užklausos. Serverio pusėje apsaugą papildo allow_url_include=Off, open_basedir ir ribojančios failų teisės.

Kas yra Local File Inclusion PHP?

Daug senesnių PHP programų puslapius įkelia pagal paprastą šabloną: parametras adrese nustato, kuris failas įtraukiamas. Jei šis parametras nepatikrintas perduodamas include, užpuolikas gali pareikalauti kitų failų serveryje. Nekenksmingesniu atveju jis nuskaito konfigūracijos failus su prisijungimo duomenimis. Blogesniu atveju jis priverčia PHP vykdyti turinį, kurį prieš tai patalpino kitur, pavyzdžiui, įkėlime ar žurnalo faile.

Vadovybei svarbu: šis pažeidžiamumas retai glūdi CMS branduolyje, dažniausiai nuosavuose kūriniuose, senuose plėtiniuose ar greitai sukurtuose papildomuose puslapiuose. Kasdien jis nepastebimas, nes svetainė veikia įprastai.

Remote File Inclusion atveju PHP atsisiunčia kodą iš svetimo adreso. Tam reikia, kad allow_url_include būtų įjungtas. Dabartinėse PHP versijose šis nustatymas pagal numatymą išjungtas, bet senuose ar klaidingai sukonfigūruotuose serveriuose jį dar galima rasti.

Tipiniai pažeidžiami šablonai kode

Pavojingos vietos, kuriose reikšmė iš užklausos, slapukų ar duomenų bazės tampa failo kelio dalimi. Dažni pavyzdžiai yra kalbos perjungikliai, kurie kalbos failą įkelia pagal parametrą, puslapių pasirinkimas pagal parametrą adrese, šablonų ar temų pasirinkimas ir dokumentų atsisiuntimo skriptai. Prie kelio pridėtas .php pabaigoje čia nepatikimai apsaugo, nes užpuolikai gali pereiti prie esamų PHP failų.

Pirmą apžvalgą gausite paieška įtraukimų, kuriuose yra kintamųjų. Ji pateiks ir nekenksmingų radinių, bet parodys, kur reikia žiūrėti atidžiau:

grep -rnE --include='*.php' '(include|require)(_once)?\s*\(?\s*\$' /var/www/projekt \
  | grep -v '/vendor/'

Kiekvieną radinį patikrinkite: iš kur kintamasis, ar lankytojas gali jį paveikti.

Saugus šablonas: leistinų sąrašas vietoj kelio iš užklausos

Patikimiausia apsauga yra tokia: lankytojas pasirenka tik raktą, o programa pati nusprendžia, kuris failas jam priklauso. Kelias niekada nesudaromas iš įvesties.

<?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];

Šis šablonas trumpas, lengvai patikrinamas ir nepalieka laisvės. Šiuolaikiniuose karkasuose šią užduotį perima maršrutizatorius (router); ir ten valdikliai bei šablonai niekada neturėtų būti nustatomi iš laisvų parametrų.

Kai failų pavadinimai turi būti dinamiški: realpath ir bazinis katalogas

Kartais fiksuotas sąrašas nepraktiškas, pavyzdžiui, atsisiuntimams iš dokumentų aplanko. Tuomet išspręstą kelią tikrinkite pagal fiksuotą bazinį katalogą ir leiskite tik paprastus failų pavadinimus.

<?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;
}

// Pateikti, o ne įtraukti: readfile() kodo nevykdo.
$datei = sichererPfad((string) ($_GET['datei'] ?? ''));
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . basename($datei) . '"');
readfile($datei);

Du dalykai lemiamai svarbūs. Reguliarusis reiškinys neleidžia katalogų skirtukų ir specialiųjų simbolių. O failai, kuriuos reikia tik pateikti, skaitomi su readfile(), o ne su include, kad joje esantis kodas niekada nebūtų įvykdytas. str_starts_with() galima naudoti nuo PHP 8.0.

Apsauga serverio pusėje: php.ini, open_basedir ir teisės

Saugus kodas yra pirmoji gynybos linija. Serverio konfigūracija užtikrina, kad praleista klaida padarytų mažiau žalos.

; php.ini arba FPM baseinas (php_admin_value / php_admin_flag)
allow_url_include = Off
allow_url_fopen   = Off   ; tik jei programa neįkelia išorinių URL
open_basedir      = /var/www/kunde-a/:/tmp/kunde-a/
display_errors    = Off
log_errors        = On

allow_url_fopen kai kurioms programoms reikia atnaujinimams ar sąsajoms. Išjungimą pirmiausia išbandykite testinėje aplinkoje. open_basedir apriboja PHP savo svetainės katalogais, o tai ypač svarbu serveriuose su keliais klientais. Smulkmenos pateiktos vadovuose open_basedir ir izoliacija kiekvienai svetainei ir Kaip saugiai sukonfigūruoti php.ini.

Papildomai įkėlimų ir talpyklos katalogai neturėtų būti vykdomi, o žurnalo failai neturėtų būti žiniatinklio šaknyje. Taip užpuolikui trūksta vietų, kur jis galėtų patalpinti savo turinį įtraukimui.

Kaip žurnaluose atpažinti išnaudojimo bandymus

Bandymai išnaudoti File Inclusion dažniausiai palieka pėdsakų prieigos žurnale. Krenta į akis parametrai, turintys katalogų pakeitimus, absoliučius sistemos kelius, srautų apvalkalus (stream wrapper) ar pilnus svetimus adresus, ypač parametruose, tokiuose kaip page, file, template ar lang. Pavienis radinys dažniausiai yra automatizuotas skenavimas. Svarbu, ar tokia užklausa buvo atsakyta būsenos kodu 200 ir neįprastu atsakymo dydžiu.

Kaip čia elgtis sistemingai, aprašo Kaip išanalizuoti žiniatinklio serverio žurnalus po atakos. Radę užuominų apie sėkmingą prieigą, laikykite, kad konfigūracijos failai buvo perskaityti, ir pakeiskite juose esančius prisijungimo duomenis.

Tai visų pirma duomenų bazės slaptažodžiai, mokėjimo ir pristatymo paslaugų raktai, SMTP prieigos ir CMS slaptieji raktai. Patikrinkite ir tai, ar po pirmos įtartinos prieigos nesusikūrė naujų failų. Kas galėjo skaityti failus, galbūt bandė ir patalpinti savus. Tuomet kalba eina jau ne tik apie kodo spragą, o apie saugumo incidentą, kurį reikėtų tvarkyti struktūruotai.

Pataisymus užtikrinti ilgam

Po pataisymo verta apsaugos funkciją pagrįsti automatizuotais testais. Paprastas testas puslapių pasirinkimui perduoda nežinomas reikšmes, katalogų pakeitimus ir tuščias įvestis ir visais atvejais tikisi numatytojo puslapio arba 404 klaidos. Tokie testai neleidžia vėlesniam pakeitimui nepastebimai vėl atverti spragos.

Vadovybei ir projektų vadovams naudingas klausimas agentūrai: ar programoje yra vietų, kur failai įkeliami pagal parametrus, ir kaip jos apsaugotos? Konkretus atsakymas su radimo vietomis ir testais yra geras ženklas.

Kaip PHP SOS padeda konkrečiai?

  • Jūsų kode tikslingai ieškome include, require ir failų funkcijų su įvestimis iš užklausos ir kiekvieną radinį vertiname.
  • Nustatome allow_url_include, open_basedir ir tinkamas teises kiekvienai svetainei ir patikriname, kad programa po to toliau veiktų.
  • Analizuojame žurnalus dėl išnaudojimo bandymų ir išsiaiškiname, ar reikia pakeisti prisijungimo duomenis.
  • Nepriklausomai patikrai siūlome PHP programų saugumo auditą su skverbties testu.

Dažni klausimai

Kuo skiriasi LFI ir RFI?

Local File Inclusion atveju programa įkelia failą, kuris jau yra serveryje, bet neturėtų būti įkeltas. Remote File Inclusion atveju kodas atsisiunčiamas iš svetimo adreso. RFI reikalauja nesaugaus PHP nustatymo, LFI ne.

Ar pakanka iš parametro pašalinti ../?

Ne. Tokius filtrus dažnai galima apeiti kodavimu ar variantais, o absoliutūs keliai praleidžiami. Saugu tik fiksuotas leistinų reikšmių ir failų susiejimas arba išspręsto kelio patikra pagal bazinį katalogą.

Ar open_basedir visiškai apsaugo nuo File Inclusion?

Ne, jis tik apriboja katalogus, prie kurių PHP gali kreiptis. Tų ribų viduje pažeidžiamumą vis tiek galima išnaudoti. open_basedir yra papildomas apsaugos sluoksnis ir nepakeičia saugaus kodo.

Ar CMS, tokius kaip WordPress, LFI paliečia?

Įprastų CMS branduolys šiuo požiūriu laikomas gerai patikrintu. Pažeidžiamumai dažniau atsiranda plėtiniuose, temose ir nuosavuose kūriniuose. Todėl svarbūs plėtinių atnaujinimai ir jų saugumo pranešimų peržiūra.

Šaltiniai

Skubus kontaktas

Kiekviena valanda svarbi. Parašykite mums dabar.

Trumpai aprašykite sistemą, PHP versiją ir pastebėjimus. Mes atsakysime, išsiaiškinsime padėtį ir dirbsime nuotoliu, kol incidentas bus užbaigtas ir dokumentuotas.

Skubi pagalba