# Command Injection'i vältimine PHP-s

> Command Injection'i vältimine PHP-s: shelli vältimine, proc_open massiiviga, escapeshellarg õigesti kasutada ja ohtlike kutsete leidmine koodist.

Allikas: https://phpsos.de/et/blog/command-injection-php/  
Seis: 25.10.2026  
Pakkuja: PHP SOS, PHP hädaabi ja serveri turvalisus. Kohene abi WhatsAppis: https://wa.me/491736494914?text=Tere%2C%20PHP%20SOS%2C%20vajame%20abi%20oma%20PHP-serveriga.%20Ettev%C3%B5te%3A%20%E2%80%A6%20Veebisait%3A%20%E2%80%A6

**Lühidalt:** Command Injection'i hoiate PHP-s kõige kindlamalt ära, kui ei kutsu shelli üldse: paljud ülesanded täidavad PHP funktsioonid või teegid otse. Kus on vaja välist programmi, annate käsu PHP 7.4-st alates massiivina funktsioonile `proc_open()`, siis käivitab PHP programmi ilma shellita. Väärtusi kontrollite lubatud väärtuste nimekirja vastu, lõpetate valikud märgiga `--` ja laete protsessi töötama minimaalsete õigustega.

## Miks on süsteemikutsel nii palju võimu

Pilditöötlus, PDF-i loomine, arhiveerimine, viirusekontroll, DNS-kirje päring: selliste ülesannete jaoks kutsuvad PHP-rakendused meelsasti väliseid programme. Tehniliselt on see mugav, piisab ühest `exec()` või `shell_exec()` reast. Turvatehniliselt on see koodi üks ohtlikumaid kohti.

Kui käsk pannakse tekstina kokku ja selle täidab shell, otsustab shell, mis on käsk ja mis andmed. Kui väljastpoolt tuleb väärtus kontrollimata sisse, võib see käsu tähendust muuta. Halvimal juhul on tagajärg sama mis PHP2Shelli puhul: suvaliste käskude täitmine serveris PHP protsessi õigustega.

Vahe: PHP2Shelli kõrvaldate PHP uuendusega. Command Injection oma koodis jääb alles pärast iga uuendust. Lisaks asuvad sellised kohad sageli abiskriptides, mis olid mõeldud ainult administraatoritele, näiteks hoolduseks, diagnostikaks või impordiks, ning jäävad hiljem kaitseta kättesaadavaks.

## Kus Command Injection PHP-s tekib

Ohus on iga koht, kus PHP käivitab shelli või programmi. Selleks mõeldud funktsioonid on:

| Funktsioon | Kas kasutab shelli? | Märkus |
|---|---|---|
| `exec()`, `system()`, `passthru()`, `shell_exec()` | jah | käsk tekstina, shell tõlgendab seda |
| Backtick-operaator | jah | vastab funktsioonile `shell_exec()` |
| `popen()` | jah | käsk tekstina |
| `proc_open()` stringiga | jah | käsk tekstina |
| `proc_open()` massiiviga | ei | PHP 7.4-st, argumendid antakse otse üle |
| `mail()` viienda parameetriga | kaudselt | lisaparameetrid meiliprogrammile |

Tüüpiline ebaturvaline muster näeb välja nii. Failinimi tuleb päringust ja satub otse käsureale:

```php
<?php
// EBATURVALINE: päringust pärit väärtus saab shelli käsu osaks
$datei = $_POST['datei'];
exec('convert uploads/' . $datei . ' -resize 800x800 vorschau/' . $datei);
```

## Esimene valik: üldse mitte shelli

Kõige tõhusam kaitse on süsteemikutse asendamine. OWASP nimetab seda selgesõnaliselt esimeseks kaitseliiniks. Paljude ülesannete jaoks on PHP-s olemas omad funktsioonid või laiendused:

- Kaustad ja failid: `mkdir()`, `copy()`, `rename()`, `unlink()`
- Arhiivid: `ZipArchive` või `PharData`
- Pildid: GD või Imagick PHP laiendusena
- Võrgupäringud: `dns_get_record()`, `gethostbyname()`, cURL
- Kontrollsummad: `hash_file()`

Sellega kaob küsimus, milliseid märke shell tõlgendab, täielikult. Lisaks saab süsteemikäskude funktsioone siis konfiguratsioonis keelata, nagu kirjeldab juhend [disable_functions õigesti kasutada](https://phpsos.de/et/ratgeber/disable-functions/).

## Kui väline programm on vajalik: proc_open massiiviga

PHP 7.4-st aktsepteerib `proc_open()` käsku massiivina. PHP käsiraamatu kohaselt käivitatakse protsess siis otse, ilma shelli ringkäiguta, ja PHP võtab enda peale argumentide vajaliku maskeerimise. Märkidel nagu semikoolon või toru pole siis enam erimärgitust.

```php
<?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);
```

Kolm asja teevad näite töökindlaks: programm on täieliku teega koodis fikseeritud, failinimi kontrollitakse kitsa mustri vastu ja iga muutuv argument algab absoluutse teega, seega ei saa seda valikuna lugeda. Kus väärtus seisab üksi, lõpetage valikud märgiga `--`, kui programm seda dokumentatsiooni järgi toetab, või lükake tagasi väärtused, mis algavad sidekriipsuga.

Ülesannete puhul, mis vajavad regulaarselt väliseid programme, näiteks suuremahuline pildi- või PDF-töötlus, tasub lahti siduda: veebirakendus paneb ainult töö järjekorda, eraldi taustateenus oma kasutaja ja õigustega täidab selle. Internetist tulevatele päringutele vastav PHP protsess ei vaja siis enam süsteemikäskude funktsioone ja need saab tema jaoks keelata. Välistel programmidel peaksid lisaks olema aja- ja mälupiirangud, et üksik töö serverit ei blokeeriks.

Erijuht on `mail()`. Viies parameeter annab lisavalikud süsteemi meiliprogrammile. Vormidest pärit väärtused, näiteks saatja aadress, ei tohi sinna kunagi kontrollimata sattuda. Töökindlam on meiliteek, mis saadab SMTP kaudu ega vaja süsteemikutset.

## escapeshellarg: õigesti kasutada ja piire teada

Kus olemasolev kood töötab veel käsurealiinidega, on `escapeshellarg()` iga üksiku väärtuse jaoks miinimummeede. Funktsioon paneb väärtuse jutumärkidesse, nii et shell käsitleb seda täpselt ühe argumendina. `escapeshellcmd()` ei sobi kasutaja sisendile, sest see lubab lisaparameetreid, nagu OWASP näitega näitab.

`escapeshellarg()` olulised piirid:

1. **Argumendi-injection jääb võimalikuks:** Maskeeritud väärtust, mis algab sidekriipsuga, loeb sihtprogramm siiski valikuna.
2. **Windows käitub teisiti:** Seal asendab funktsioon käsiraamatu kohaselt teatud märgid, nagu `%` ja `"`, tühikutega; väärtused jõuavad kohale muudetuna.
3. **Märgistik:** Olenevalt serveri locale-sättest võivad mitte-ASCII märgid kaduda.

## Leidude otsimine olemasolevast koodist

Süsteemikutsete otsing käib kiiresti ja annab ülevaatliku nimekirja:

```bash
grep -rnE "\b(exec|system|passthru|shell_exec|popen|proc_open)\s*\(" src/ --include="*.php"
grep -rn '`' src/ --include="*.php" | grep -v '^\s*//'
```

Iga leid vajab vastust kolmele küsimusele: Kas kutse saab asendada PHP funktsioonidega? Kas väljastpoolt voolab väärtusi sisse? Milliste õigustega protsess töötab? Viimane küsimus on otsustav kahju jaoks hädaolukorras. Oma kasutajaga ja koodile kirjutusõigusteta PHP protsess teeb vähem kahju, vt [Vähima õiguse põhimõte: veebiserveri kasutaja õigused](https://phpsos.de/et/blog/rechte-des-webserver-benutzers/).

> **Oluline:**
> Kui PHP-FPM-i või veebiserveri protsess käivitab shelli, on see tugev hoiatusmärk sissemurdmisest. Kuidas seda ära tunda, kirjeldab [Hoiatusmärk: veebiserver käivitab shelli](https://phpsos.de/et/blog/webserver-startet-shell/).

## Kuidas PHP SOS konkreetselt aitab?

- Me otsime kõik süsteemikutsed teie rakendusest, pistikprogrammidest ja abiskriptidest.
- Me asendame kutsed võimaluse korral PHP funktsioonidega ja viime ülejäänud üle kutsetele ilma shellita.
- Me keelame PHP konfiguratsioonis mittevajalikud funktsioonid ja kontrollime PHP kasutaja õigusi.
- Me testime muudatused ja dokumenteerime, milliseid programme rakendus tohib veel kutsuda.

Oma tarkvara kontrolli pakume [turvaauditi ja läbitungimistestina](https://phpsos.de/et/leistungen/sicherheitsaudit-penetrationstest/).


## Korduma kippuvad küsimused

**Kas escapeshellarg piisab Command Injection'i vastu?**  
See takistab väärtuse käivitamist järgmise käsuna. See ei takista, et kutsutud programm loeb väärtust valikuna, kui see algab sidekriipsuga. Seetõttu kontrollige väärtusi lisaks ja lõpetage valikud, kus programm seda toetab, märgiga --.

**Mis vahe on escapeshellarg'il ja escapeshellcmd'il?**  
escapeshellarg käsitleb üksikut väärtust täpselt ühe argumendina. escapeshellcmd maskeerib ainult metamärgid kogu käsureal ja lubab lisaparameetreid. Kasutaja sisendi jaoks on escapeshellarg õige valik, kutse ilma shellita on veel parem.

**Kas peaksin exec'i ja system'i disable_functions abil keelama?**  
Kui teie rakendus neid ei vaja, siis jah. See piirab kahju pärast sissemurdmist. See ei asenda aga turvalist koodi, sest käske vajavad rakendused vajavad neid funktsioone edasi.

**Kuidas ma tunnen ära, kas Command Injection'it on ära kasutatud?**  
Tugev hoiatusmärk on PHP või veebiserveri protsessid, mis käivitavad shelli, nagu sh või bash. Lisaks tundmatud protsessid, uued failid üleslaadimiskaustades ja ebatavalised väljaminevad ühendused.

## Allikad

- [OWASP OS Command Injection Defense Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html)
- [PHP käsiraamat: proc_open](https://www.php.net/manual/de/function.proc-open.php)
- [PHP käsiraamat: escapeshellarg](https://www.php.net/manual/de/function.escapeshellarg.php)
