Aktuālā situācija: PHP2Shell. Divas kritiskas PHP ievainojamības (CVSS 9.2) ļauj izpildīt ļaunprātīgu kodu bez pieteikšanās. Ielāpi kopš 21.10.2026.Kas jādara tagad →

Command Injection novēršana PHP

Kā izstrādātāji PHP novērš Command Injection: sistēmas komandas aizstāt ar PHP funkcijām, lietot proc_open bez shell, pārbaudīt argumentus un atrast bīstamās vietas esošajā kodā.

Aktuāls uz 5 min lasīšanas laiksDroša izstrāde

Īsi sakot: Command Injection PHP visdrošāk novērš, vispār neizsaucot shell: daudzus uzdevumus tieši veic PHP funkcijas vai bibliotēkas. Kur ārēja programma nepieciešama, komandu kopš PHP 7.4 nododiet kā masīvu proc_open(), tad PHP programmu startē bez shell. Vērtības pārbaudiet pret atļauju sarakstu, opcijas noslēdziet ar -- un procesu darbiniet ar minimālām tiesībām.

Kāpēc sistēmas izsaukumam ir tik liela vara

Attēlu apstrāde, PDF izveide, arhivēšana, vīrusu skenēšana, DNS ieraksta vaicājums: šādiem uzdevumiem PHP lietojumprogrammas labprāt izsauc ārējas programmas. Tehniski tas ir ērti, pietiek ar vienu rindiņu ar exec() vai shell_exec(). Drošības ziņā tā ir viena no bīstamākajām vietām kodā.

Ja komanda tiek salikta kā teksts un izpildīta ar shell, shell nosaka, kas ir komanda un kas dati. Ja vērtība no ārpuses nonāk tur nepārbaudīta, tā var mainīt komandas nozīmi. Sliktākajā gadījumā sekas ir tādas pašas kā PHP2Shell: patvaļīgu komandu izpilde serverī ar PHP procesa tiesībām.

Atšķirība: PHP2Shell novērš ar PHP atjauninājumu. Command Injection savā kodā paliek pēc katra atjauninājuma. Klāt nāk tas, ka šādas vietas bieži slēpjas palīgskriptos, kas bija paredzēti tikai administratoriem, piemēram, apkopei, diagnostikai vai importam, un vēlāk paliek sasniedzami bez aizsardzības.

Kur Command Injection rodas PHP

Apdraudēta ir katra vieta, kur PHP startē shell vai programmu. Funkcijas tam ir:

Funkcija Izmanto shell? Piezīme
exec(), system(), passthru(), shell_exec() jā komanda kā teksts, shell to interpretē
Backtick operators jā atbilst shell_exec()
popen() jā komanda kā teksts
proc_open() ar virkni jā komanda kā teksts
proc_open() ar masīvu nē kopš PHP 7.4, argumenti tiek nodoti tieši
mail() ar piekto parametru netieši papildu parametri pasta programmai

Tipisks nedrošs paraugs izskatās šādi. Faila nosaukums nāk no pieprasījuma un nonāk tieši komandrindā:

<?php
// NEDROŠI: vērtība no pieprasījuma kļūst par shell komandas daļu
$datei = $_POST['datei'];
exec('convert uploads/' . $datei . ' -resize 800x800 vorschau/' . $datei);

Pirmā izvēle: nekāda shell

Efektīvākā aizsardzība ir sistēmas izsaukuma aizstāšana. OWASP to nepārprotami min kā pirmo aizsardzības līniju. Daudziem uzdevumiem PHP ir savas funkcijas vai paplašinājumi:

  • Direktoriji un faili: mkdir(), copy(), rename(), unlink()
  • Arhīvi: ZipArchive vai PharData
  • Attēli: GD vai Imagick kā PHP paplašinājums
  • Tīkla vaicājumi: dns_get_record(), gethostbyname(), cURL
  • Kontrolsummas: hash_file()

Tā jautājums, kuras rakstzīmes shell interpretē, pazūd pilnībā. Turklāt sistēmas komandu funkcijas tad var bloķēt konfigurācijā, kā apraksta ceļvedis disable_functions pareiza lietošana.

Ja ārēja programma nepieciešama: proc_open ar masīvu

Kopš PHP 7.4 proc_open() pieņem komandu kā masīvu. Saskaņā ar PHP rokasgrāmatu process tad tiek startēts tieši, bez līkloča caur shell, un PHP veic nepieciešamo argumentu maskēšanu. Tādām rakstzīmēm kā semikols vai pipe tad vairs nav īpašas nozīmes.

<?php
declare(strict_types=1);

$name = basename((string) ($_POST['datei'] ?? ''));
if (!preg_match('/^[a-z0-9_-]{1,64}\.(jpg|png)$/', $name)) {
    throw new InvalidArgumentException('Ungültiger Dateiname.');
}

$befehl = ['/usr/bin/convert', '/srv/app/uploads/' . $name, '-resize', '800x800',
           '/srv/app/vorschau/' . $name];

$prozess = proc_open($befehl, [1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
if (!is_resource($prozess)) {
    throw new RuntimeException('Programm konnte nicht gestartet werden.');
}
$fehlerausgabe = stream_get_contents($pipes[2]);
$status = proc_close($prozess);

Piemēru izturīgu dara trīs lietas: programma ar pilnu ceļu ir fiksēta kodā, faila nosaukums tiek pārbaudīts pret šauru paraugu, un katrs mainīgais arguments sākas ar absolūtu ceļu, tātad to nevar nolasīt kā opciju. Kur vērtība stāv viena pati, opcijas noslēdziet ar --, ja programma to saskaņā ar dokumentāciju atbalsta, vai noraidiet vērtības, kas sākas ar defisi.

Uzdevumiem, kam regulāri vajadzīgas ārējas programmas, piemēram, attēlu vai PDF apstrādei lielākā apjomā, vērts atdalīt: tīmekļa lietojumprogramma tikai ieliek uzdevumu rindā, atsevišķs fona pakalpojums ar savu lietotāju un savām tiesībām to izpilda. PHP procesam, kas atbild uz pieprasījumiem no interneta, tad sistēmas komandu funkcijas vairs nav vajadzīgas, un tās tam var bloķēt. Ārējām programmām jābūt arī laika un atmiņas robežām, lai viens uzdevums neapturētu serveri.

Īpašs gadījums ir mail(). Piektais parametrs nodod papildu opcijas sistēmas pasta programmai. Vērtības no veidlapām, piemēram, sūtītāja adrese, tur nekad nedrīkst nonākt nepārbaudītas. Izturīgāka ir pasta bibliotēka, kas sūta pa SMTP un nevajag sistēmas izsaukumu.

escapeshellarg: pareiza lietošana un robežu zināšana

Kur esošajā kodā vēl strādā ar komandrindām, escapeshellarg() katrai atsevišķai vērtībai ir minimālais pasākums. Funkcija vērtību ieliek pēdiņās, lai shell to apstrādātu kā tieši vienu argumentu. escapeshellcmd() lietotāja ievadei nav piemērots, jo pieļauj papildu parametrus, kā OWASP rāda piemērā.

Svarīgas escapeshellarg() robežas:

  1. Argumentu injekcija joprojām iespējama: maskēta vērtība, kas sākas ar defisi, mērķa programmai tik un tā tiek nolasīta kā opcija.
  2. Windows uzvedas citādi: tur funkcija saskaņā ar rokasgrāmatu noteiktas rakstzīmes, piemēram, % un ", aizstāj ar atstarpēm; vērtības tātad nonāk mainītas.
  3. Rakstzīmju kopa: atkarībā no servera Locale iestatījuma ne-ASCII rakstzīmes var zust.

Atradumu vietu atrašana esošajā kodā

Sistēmas izsaukumu meklēšana ir ātri izdarāma un dod pārskatāmu sarakstu:

grep -rnE "\b(exec|system|passthru|shell_exec|popen|proc_open)\s*\(" src/ --include="*.php"
grep -rn '`' src/ --include="*.php" | grep -v '^\s*//'

Katram atradumam vajadzīga atbilde uz trim jautājumiem: vai izsaukumu var aizstāt ar PHP funkcijām? Vai iekšā plūst vērtības no ārpuses? Ar kādām tiesībām process darbojas? Pēdējais jautājums ir izšķirošs kaitējumam smagākajā gadījumā. PHP process ar savu lietotāju un bez rakstīšanas tiesībām uz kodu nodara mazāk, skatiet Least Privilege: tīmekļa servera lietotāja tiesības.

Svarīgi

Ja PHP-FPM vai tīmekļa servera process startē shell, tas ir spēcīgs brīdinājuma signāls par ielaušanos. Kā to atpazīt, apraksta Brīdinājuma signāls: tīmekļa serveris startē shell.

Kā PHP SOS palīdz konkrēti?

  • Mēs meklējam visus sistēmas izsaukumus jūsu lietojumprogrammā, spraudņos un palīgskriptos.
  • Mēs, kur iespējams, aizstājam izsaukumus ar PHP funkcijām un pārējos pārejam uz izsaukumiem bez shell.
  • Mēs bloķējam nevajadzīgās funkcijas PHP konfigurācijā un pārbaudām PHP lietotāja tiesības.
  • Mēs testējam izmaiņas un dokumentējam, kuras programmas lietojumprogramma vēl drīkst izsaukt.

Savas programmatūras pārbaudi piedāvājam kā drošības auditu un penetrācijas testu.

Biežāk uzdotie jautājumi

Vai ar escapeshellarg pietiek pret Command Injection?

Tas novērš, ka vērtība tiek izpildīta kā cita komanda. Tas nenovērš, ka vērtību izsauktā programma nolasa kā opciju, ja tā sākas ar defisi. Tāpēc papildus pārbaudiet vērtības un, kur programma to atbalsta, noslēdziet opcijas ar --.

Kāda ir atšķirība starp escapeshellarg un escapeshellcmd?

escapeshellarg vienu vērtību uzskata par tieši vienu argumentu. escapeshellcmd maskē tikai metarakstzīmes visā komandrindā un pieļauj papildu parametrus. Lietotāja ievadei pareizā izvēle ir escapeshellarg, un izsaukums bez shell ir vēl labāks.

Vai exec un system jābloķē ar disable_functions?

Ja jūsu lietojumprogrammai tās nav vajadzīgas, jā. Tas ierobežo kaitējumu pēc ielaušanās. Taču tas neaizstāj drošu kodu, jo lietojumprogrammām, kam komandas vajadzīgas, funkcijas joprojām ir nepieciešamas.

Kā atpazīt, vai Command Injection ir izmantota?

Spēcīga brīdinājuma pazīme ir PHP vai tīmekļa servera procesi, kas startē shell, piemēram, sh vai bash. Klāt nāk nezināmi procesi, jauni faili augšupielāžu mapēs un neparasti izejošie savienojumi.

Avoti

Tūlītējs kontakts

Katra stunda ir svarīga. Rakstiet mums tūlīt.

Īsi aprakstiet sistēmu, PHP versiju un novēroto. Mēs sazināsimies, noskaidrosim situāciju un strādāsim attālināti, līdz incidents ir slēgts un dokumentēts.

Tūlītēja palīdzība