Код
#статьи

Почему опасно использовать функции system(), eval() или exec() и как защититься от инъекций кода

Разбираемся, почему одну из самых полезных возможностей современных языков программирования нужно использовать с осторожностью.

 vlada_maestro / shutterstock

В большинстве современных языков программирования есть функции, которые позволяют выполнить какую-нибудь команду операционной системы или запустить код:

  • system () в C++;
  • eval () в JavaScript;
  • exec () в PHP;
  • os.system () в Python и так далее.

Например, с помощью одной строчки на C++, содержащей system (), можно в графической оболочке Gnome показать уведомление пользователю:

//Вывод оповещения в оболочке рабочего стола GNOME для Linux
system("notify-send 'Hello, World!'");

Это очень удобно, потому что иначе пришлось бы создавать отдельное окно, указывать его расположение на экране, закрывать через определённое время и так далее.

Вы можете передать в system () любую встроенную команду операционной системы или внешнюю программу, установленную на компьютере. Они будут выполнять все те действия, на которые способна ваша ОС: создавать, удалять, перемещать файлы и папки, выключать компьютер, открывать другие программы и многое другое.

Можно, например, создать видеоредактор, почти не задумываясь о том, как происходит работа с самим файлом. Для этого достаточно с помощью system () передать программе ffmpeg необходимые аргументы. Остаётся лишь создать графический интерфейс, и приложение готово.

Почему это небезопасно

Чаще всего строка, содержащая команду, которую необходимо выполнить вызовом system (), формируется с добавлением каких-либо данных. Это может быть текст уведомления или аргументы для другой программы. Затем system () запускает командную оболочку (Bourne Shell или аналогичную) и передаёт команду ей. Это позволяет получить почти полный доступ к системе.

Важно! Если у пользователя есть возможность повлиять на передаваемые функции system () данные, он может попытаться сделать командную инъекцию (англ. Command injection или Shell injection) — добавить к данным вредоносные инструкции, которые будут выполнены в оболочке.

Рассмотрим фрагмент кода на C++:

//Спрашиваем пользователя, какое сообщение он хочет вывести
string input;
cout << "Your message: ";
//Получаем ввод
getline(cin, input);
//Формируем команду
string s = "notify-send '" + input + "'";
//Преобразуем строку в принимаемый функцией system() тип данных
const char *command = s.c_str();
//Запускаем команду 
system(command);

Кроме самого текста сообщения, взломщик может ввести и что-нибудь нежелательное и даже опасное. Например, вот такой фрагмент:

Hello! ' & & rm -rf /* & & echo 'a

В итоге оболочка получит три команды вместо одной:

  • notify-send 'Hello! ' — вывод уведомления, задуманный разработчиками.
  • rm -rf /* — удаление всех файлов на компьютере.
  • echo 'a — вывод буквы a. Эта команда нужна для того, чтобы не было конфликтов с кавычками.

Конечно, вряд ли кто-то будет делать что-нибудь подобное на своём устройстве. Но ваша программа может работать и на сервере.

Вот пример для сайта, написанный на Python:

#!/usr/bin/env python3
import os
print("Content-type: text/html")
print()
print("<h1>Hello world!</h1>")
os.system("notify-send 'Hello!'")

Каждый раз, когда пользователи будут заходить на эту страницу, на сервере будет выводиться уведомление.

Этот пример немного надуманный, потому что на серверах обычно не устанавливают оболочку рабочего стола. Но есть и другие похожие уязвимости, которые не требуют GUI, но могут использоваться для взлома.

Результат работы программы

В веб-приложениях функции вроде os.system () нужны очень редко, но последствия их небрежного использования могут оказаться весьма серьёзными: утечка данных пользователей, поломка оборудования, потеря прибыли и так далее.

Даже если программа запущена не от root и разрушительное действие напрямую невозможно, взломщик может выполнить эскалацию привилегий (англ. Privilege escalation) — с помощью известных уязвимостей получить повышенный уровень доступа к системе. Перечень таких незакрытых лазеек время от времени публикуется на различных хакерских сайтах.

Что касается языков, которые не имеют доступа к командной оболочке (например, JavaScript в браузере), то в них возможна инъекция кода (англ. Code injection) — передача вредоносного кода текущей программе.

Самый простой пример — попытка ввести JavaScript-код в какую-нибудь форму на сайте. Например, взломщик может оставить комментарий следующего вида:

Hello! <script>window.location='http://other_site/&#39; & lt; /script& gt;

Пользователи, которые будут открывать страницу с этим комментарием, будут перенаправляться на другой сайт.

Другой пример — функция eval () в JavaScript:

//Команды, которые пришли с сервера
//К основным инструкциям попал код, который делает невозможной нормальную работу сайта
commands = "Foo(); Bar(); while(true) { alert(1); }";
 
//Выполнение переданных команд
eval(commands);

Способы введения инъекции примерно те же самые.

Как защититься от инъекций кода

Если для работы вашей программы необходимы функции вроде system () или eval (), то нужно выполнить валидацию всех вводимых данных — проверить их на соответствие определённым требованиям.

Основные способы валидации и защиты от инъекций кода:

  • Создание чёрного списка. Блокируйте ввод, если значение находится в чёрном списке или частично состоит из запрещённых данных (например, Hello! , rm -rf /*).
  • Создание белого списка. Блокируйте ввод, если введённые пользователем данные не встречаются в белом списке.
  • Экранирование. Преобразуйте специальные символы в их кодовое представление (> заменять на & #62;) или экранируйте их (» заменять на \»).
  • Проверка формата. Проверяйте, чтобы сообщение вводилось в определённом формате. Например, телефонный номер может выглядеть так: 8 (800) 555-35-35.
  • Ограничение длины. Удаляйте все лишние символы, если длина строки превышает какое-либо значение.
  • Обрезка текста. Удаляйте все пробелы в начале и конце строки.
  • Используйте директиву use strict и подобные. Сообщите интерпретатору, чтобы он более строго реагировал на ошибки в коде.

В зависимости от особенностей приложения могут помочь и другие способы защиты. Например, если есть база данных, то имеет смысл использовать хранимые процедуры. Если же это онлайн-игра, то нужно проверять список подключённых DLL.

Важно! Задействовать какой-то один из перечисленных способов, скорее всего, не поможет — злоумышленник может найти другие неприкрытые лазейки.

Подходите к защите комплексно, старайтесь использовать сразу несколько методов. И не забывайте следить за тем, какие права есть у вашего приложения. Это не позволит взломщику нанести серьёзный ущерб, если ему всё же удастся ввести инъекцию кода.

Проверьте свой английский. Бесплатно ➞
Нескучные задания: small talk, поиск выдуманных слов — и не только. Подробный фидбэк от преподавателя + персональный план по повышению уровня.
Пройти тест
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована