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 →

Saugus failų įkėlimas PHP: PHP failų įkėlimo sauga

Įkėlimo formos yra dažnas kelias webshell. Kaip teisingai tikrinti failų tipus, saugiai juos laikyti ir neleisti serveriui vykdyti įkeltų skriptų.

Būsena 5 min. skaitymoVadovai

Trumpai: PHP įkėlimų saugą lemia ne viena patikra, o derinys: failo tipą tikrinti serveryje, failus pervardyti, laikyti už žiniatinklio šaknies ribų ir įkėlimų aplanke neleisti vykdyti skriptų. Netikrinta įkėlimo forma yra vienas klasikinių kelių, kuriuo užpuolikai į žiniatinklio serverį įneša užpakalines duris.

Kodėl įkėlimo formos yra mėgstamiausi įėjimo vartai

Kiekviena forma, priimanti failus, į jūsų serverį rašo svetimus duomenis. Prie jų priskiriamos darbo paraiškų formos, klientų portalai, paveikslėlių galerijos, pagalbos sritys ir daug CMS bei parduotuvių plėtinių. Jei užpuolikui pavyksta ten patalpinti failą, kurį žiniatinklio serveris paskui vykdo kaip PHP, jis turi nuolatines užpakalines duris.

Vadovybei tai reiškia: atrodytų nekaltas gyvenimo aprašymų įkėlimas blogiausiu atveju gali lemti viso žiniatinklio serverio perėmimą. Be to, įkėlimų aplankuose dažnai yra asmens duomenų, kurie neteisingai sukonfigūravus yra viešai pasiekiami.

Administratoriams ir kūrėjams tai reiškia: programos kodas ir serverio konfigūracija turi derėti. Vieno lygmens silpnumą dažnai sulaiko kitas, jei abu įgyvendinti tvarkingai. Kaip rasti jau įterptus failus, aprašo vadovas Webshell aptikimas ir šalinimas.

PHP įkėlimų saugos pagrindinės taisyklės

Toliau pateiktos taisyklės atitinka OWASP Cheat Sheets rekomendacijas ir pasiteisino veikloje:

  • Leistinų sąrašas, o ne draudžiamųjų: priimti tik aiškiai leistinus failų tipus, pavyzdžiui, PDF, JPG ir PNG. Draudimų sąrašus atskiriems plėtiniams galima apeiti variantais ir dvigubais plėtiniais.
  • Tikrinti turinį, o ne tik plėtinį: tikrąjį tipą serveryje nustatyti su finfo. MIME tipas $_FILES yra siuntėjo nurodymas ir nepatikimas.
  • Naujas failo pavadinimas: originalaus pavadinimo niekada nenaudoti kaip failo pavadinimo. Sugeneruoti atsitiktinį pavadinimą ir pridėti plėtinį iš leistinų sąrašo.
  • Saugojimas už žiniatinklio šaknies ribų: failus laikyti taip, kad jų nebūtų galima pasiekti tiesiogiai per URL. Pateikimas vyksta per skriptą su teisių patikra.
  • Riboti dydį: programoje, PHP ir žiniatinklio serveryje.
  • Jokio vykdymo: jei įkėlimai vis tiek turi būti žiniatinklio šaknyje, tame kataloge išjungti skriptų vykdymą.
  • Patikra tik serveryje: JavaScript patikros naršyklėje pagerina naudojimą, bet neapsaugo.

Saugus pavyzdys su finfo ir atsitiktiniu pavadinimu

Toliau pateiktas pavyzdys rodo saugaus apdorojimo esmę. Jis sąmoningai trumpas ir turi būti įterptas į jūsų programą, t. y. su prisijungimu, teisių patikra, CSRF apsauga ir registravimu.

<?php
declare(strict_types=1);

const MAX_BYTES  = 5 * 1024 * 1024; // 5 MB
const ERLAUBT    = [
    'application/pdf' => 'pdf',
    'image/jpeg'      => 'jpg',
    'image/png'       => 'png',
];
const ZIELORDNER = '/var/www/daten/uploads'; // už žiniatinklio šaknies ribų

$datei = $_FILES['dokument'] ?? null;

if (!is_array($datei) || $datei['error'] !== UPLOAD_ERR_OK) {
    http_response_code(400);
    exit('Upload fehlgeschlagen.');
}

if ($datei['size'] > MAX_BYTES || !is_uploaded_file($datei['tmp_name'])) {
    http_response_code(400);
    exit('Datei ungültig.');
}

$finfo = new finfo(FILEINFO_MIME_TYPE);
$typ   = $finfo->file($datei['tmp_name']);

if (!isset(ERLAUBT[$typ])) {
    http_response_code(415);
    exit('Dateityp nicht erlaubt.');
}

$name = bin2hex(random_bytes(16)) . '.' . ERLAUBT[$typ];
$ziel = ZIELORDNER . '/' . $name;

if (!move_uploaded_file($datei['tmp_name'], $ziel)) {
    http_response_code(500);
    exit('Speichern fehlgeschlagen.');
}

chmod($ziel, 0640);
// Originalų pavadinimą saugoti tik kaip tekstą duomenų bazėje
// ir kiekvienos išvesties metu apdoroti su htmlspecialchars().

Paveikslėliams verta atlikti dar vieną žingsnį: paveikslėlį iš naujo užkoduoti su GD ar Imagick ir metaduomenis atmesti. Tai nepakeičia kitų patikrų, bet sumažina likutinę riziką dėl įterptų svetimų duomenų.

Dokumentams iš nežinomų šaltinių, pavyzdžiui, darbo paraiškoms ar skundų nuotraukoms, prieš perdavimą prasminga papildomai patikrinti antivirusine programa. Ji saugo ne tiek žiniatinklio serverį, kiek darbuotojus, kurie failus vėliau atidaro savo darbo kompiuteriuose. Be to, registruokite kiekvieną įkėlimą su laiku, paskyra, failo dydžiu ir nustatytu tipu, kad neįprasti šablonai vėliau būtų atsekami.

CMS ir parduotuvėse įmontuotos medijos bibliotekos dažniausiai turi savų patikrų. Rizikingesni yra formų plėtiniai, importo funkcijos ir pačių sukurti papildomi puslapiai, kurie failus saugo apeidami biblioteką. Būtent šias vietas atliekant patikrą reikėtų pažiūrėti pirmiausia.

Patarimas

Failus iš apsaugoto aplanko pateikite su Content-Disposition: attachment ir X-Content-Type-Options: nosniff. Tuomet naršyklė problemiško turinio nerodo tiesiogiai, o siūlo jį kaip atsisiuntimą.

PHP vykdymo įkėlimų aplanke išjungimas

Net jei failas su klaidingu turiniu praslysta, žiniatinklio serveris jo vykdyti kaip skripto neturi. Šis antrasis apsaugos sluoksnis priklauso kiekvienam serveriui, kuris saugo įkėlimus žiniatinklio šaknyje.

Nginx su PHP-FPM:

location ^~ /uploads/ {
    location ~* \.(php|phtml|phar)$ {
        deny all;
    }
}

Apache, geriausia tiesiai VirtualHost, o ne .htaccess, kad taisyklės programa negalėtų perrašyti:

<Directory "/var/www/html/uploads">
    AllowOverride None
    <FilesMatch "\.(php|phtml|phar)$">
        Require all denied
    </FilesMatch>
</Directory>

Jei PHP veikia kaip Apache modulis (mod_php), tame pačiame bloke galite papildomai nustatyti php_admin_flag engine off. Po pakeitimo su nekenksminga bandomąja byla, kuri tik išveda tekstą, patikrinkite, ar serveris į užklausą atsako 403.

Dydžio ribos ir ryšys su PHP2Shell

Įkėlimai glaudžiai susiję su PHP nustatymais post_max_size ir upload_max_filesize. Dėl PHP2Shell php.net kaip pereinamąją priemonę rekomenduoja post_max_size nustatyti 8M, jei įkėlimų nereikia, arba 32M su įkėlimais, o upload_max_filesize laikyti mažesnį. Tai nėra visiška apsauga ir nepakeičia atnaujinimo į ištaisytą PHP versiją.

; php.ini arba FPM baseinas (php_admin_value), po to iš naujo paleisti visus darbinius procesus
file_uploads        = On
post_max_size       = 32M
upload_max_filesize = 20M
max_file_uploads    = 5

Jei svetainei įkėlimų visai nereikia, file_uploads = Off yra tvarkingiausias sprendimas. Kokie nustatymai prasmingi iki pataisos, aprašyta puslapyje Skubios priemonės be pataisos. Tinkamas įkėlimų katalogų teises aprašo Failų teisės žiniatinklio serveryje.

Tipinės įkėlimo funkcijų klaidos

Peržiūrint esamas programas nuolat kartojasi tie patys šablonai. Savo formas tikslingai patikrinkite dėl šių punktų:

  • Plėtinys tikrinamas tik iš dalies, todėl praslysta failų pavadinimai su keliais plėtiniais.
  • Originalus pavadinimas iš formos patenka į saugojimo kelią. Tai atveria kelius kelio manipuliacijai ir esamų failų perrašymui.
  • Įkėlimai viešai laikomi su nuspėjamais pavadinimais. Paraiškoms ar sutartims tai yra ir duomenų apsaugos problema.
  • Įkėlimų aplankas žiniatinklio serveriui rašomas ir kartu vykdomas.
  • Seni įkėlimo plėtiniai ar formų įskiepiai neatnaujinami, nors gamintojas išleido saugumo atnaujinimus.
  • Klaidos neregistruojamos. O atmesti įkėlimai yra ankstyvas tikslinių bandymų signalas.

Kaip PHP SOS padeda konkrečiai?

  • Tikriname jūsų įkėlimo funkcijas kode ir serverio konfigūracijoje ir įvardijame konkrečius silpnumus su prioritetu.
  • Vykdymo draudimą įkėlimų aplanke, tinkamas teises ir dydžio ribas nustatome pagal serverio sutvirtinimą.
  • Įtarimo atveju peržiūrime įkėlimų katalogus dėl įterptų skriptų ir juos išvalome per savo kenkėjiškų programų šalinimą.
  • Pageidaujant įgyvendinimą nepriklausomai patikrinti leidžiame per skverbties testą.

Dažni klausimai

Ar pakanka įkeliant patikrinti failo plėtinį?

Ne. Plėtinys ir naršyklės nurodytas MIME tipas ateina iš siuntėjo ir gali būti pasirenkami laisvai. Turinį tikrinkite serveryje, suteikite naują failo pavadinimą ir uždrauskite skriptų vykdymą įkėlimų kataloge.

Kur turėtų būti saugomi įkelti failai?

Geriausia už viešojo žiniatinklio katalogo ribų. Pateikimą tuomet perima skriptas, kuris prieš tai patikrina, ar naudotojas gali matyti failą. Taip failų nei galima iškviesti tiesiogiai, nei jie vykdomi.

Ar SVG failai kaip paveikslėliai yra nekenksmingi?

Automatiškai ne. SVG yra XML ir gali turėti aktyvaus turinio. SVG leiskite tik jei failus išvalote arba pateikiate tik kaip atsisiuntimą su tinkama Content-Disposition antraštė.

Ar įkėlimo dydis turi ką nors bendra su PHP2Shell?

Taip, netiesiogiai. Kaip pereinamąją priemonę php.net rekomenduoja mažesnes post_max_size ir upload_max_filesize reikšmes. Tai mažina riziką, bet nepakeičia atnaujinimo į ištaisytą PHP versiją ir visų darbinių procesų paleidimo iš naujo.

Š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