Перейти к основному содержимому

Инъекции

Разработчику Инженеру

Инъекции

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

В основе инъекционных атак лежит недостаточная проверка, фильтрация или экранирование входных данных, предоставляемых пользователем. Злоумышленник, используя слабые места в архитектуре приложения, может манипулировать его поведением, получать доступ к конфиденциальным данным, выполнять произвольные команды на сервере, подменять контент или полностью компрометировать систему.

Инъекции не являются атакой конкретного типа — это общий паттерн уязвимости, проявляющийся в различных контекстах — базы данных, командные оболочки, языки разметки, динамические языки программирования и т.д. Ниже будут рассмотрены наиболее значимые виды инъекций, их механизмы, последствия и методы защиты.

Play ITЗагрузка интерактивного демо…


Общая модель инъекции

Любая инъекция предполагает три основных компонента:

  1. Контекст исполнения — среда, в которой интерпретируются или исполняются входные данные (например, SQL-интерпретатор, командная оболочка, JavaScript-движок браузера).
  2. Данные, контролируемые атакующим — строка, отправляемая пользователем (через параметры URL, формы, заголовки HTTP и др.), которая затем встраивается в исполняемый код без надлежащей обработки.
  3. Нарушение синтаксической или семантической границы — злоумышленник вводит специальные символы или конструкции, которые изменяют исходную логику построения запроса или команды.

Успешная инъекция происходит тогда, когда приложение не разделяет данные и код, позволяя атакующему "переписать" поведение системы с помощью внешнего ввода.

ТипКонтекст исполненияОпасные символы / приёмыЗащита
SQLiSQL-запрос', --, UNIONПараметризованные запросы
CMDishell (os.system, exec);, |, `Не вызывать shell; whitelist
XSSHTML/JS в браузере<script>, onerror=Экранирование, CSP
LDAPiLDAP-фильтр*)(Параметризация, экранирование

SQL-инъекции (SQL Injection, SQLi)

SQL-инъекция — наиболее известный и исторически значимый тип инъекции. Она возникает при некорректной передаче пользовательских данных в SQL-запросы, что позволяет атакующему изменить логику запроса к реляционной базе данных.


Механизм атаки

Рассмотрим простой пример на языке C# (аналогично проявляется в PHP, Java и других языках):

string query = "SELECT * FROM Users WHERE Login = '" + userInput + "'";

Если userInput содержит строку ' OR '1'='1, то результирующий запрос примет вид:

SELECT * FROM Users WHERE Login = '' OR '1'='1'

Это приведёт к выборке всех записей из таблицы Users, поскольку условие '1'='1' всегда истинно. Злоумышленник может не только обойти аутентификацию, но и модифицировать данные (INSERT, UPDATE, DELETE), извлекать данные из других таблиц (UNION SELECT), выполнять административные команды (в случае расширенных прав СУБД) или даже получить доступ к файловой системе.


Основные типы SQLi

Уязвимость одна — пользовательский ввод попадает в текст SQL без разделения кода и данных. На практике атаки делят по тому, как атакующий узнаёт результат — видит ли строки в ответе, ошибки СУБД, отличия страницы или задержку ответа.

ТипКак узнаётся результатТипичный приём
Тавтологическая (classic)Данные или обход фильтра в том же HTTP-ответеOR 1=1, ' OR '1'='1
In-band (union / error-based)Тот же канал: HTML страницы или текст ошибки SQLUNION SELECT, сообщения СУБД
Blind — booleanРазный вид страницы при истинном/ложном условииAND LENGTH(database())=6
Blind — time-basedВремя ответа (задержка при истине)SLEEP(5), pg_sleep(5)
Out-of-bandСторонний канал (DNS, HTTP с сервера БД)LOAD_FILE, UTL_HTTP (редко, нужны права и сеть)

Ниже — схема каждого типа на уровне "запрос приложения → что видит атакующий".

Тавтологическая инъекция (OR 1=1)

Самый наглядный случай — в условие WHERE подставляют выражение, которое всегда истинно, и закрывают остаток запроса комментарием (-- в MySQL/PostgreSQL, /* в других диалектах).

Приложение собирает запрос по ID из URL:

GET /students?studentid=117

Код на сервере (упрощённо):

SELECT * FROM students WHERE studentid = '<ввод из URL>';

Атакующий передаёт:

?studentid='117' OR 1=1; --

Итоговый запрос:

SELECT * FROM students WHERE studentid = '117' OR 1=1; --

Условие OR 1=1 истинно для каждой строки таблицы — в ответ уходят все записи, а не одна карточка студента. Тот же приём обходит форму входа (' OR '1'='1 в поле логина) и фильтры, если разработчик только "экранирует кавычки", но оставляет конкатенацию строк.

In-band — UNION и error-based

In-band означает: украденные данные или признаки атаки приходят в том же ответе, что и обычная страница (или в явной ошибке СУБД в теле HTML).

Исходный запрос каталога:

SELECT name, description FROM products WHERE category = 'Gifts';

В поле категории (или в параметре URL) вводят:

' UNION SELECT username, password FROM users --

СУБД выполняет объединённый запрос:

SELECT name, description FROM products WHERE category = 'Gifts'
' UNION SELECT username, password FROM users --';

Если число и типы столбцов в UNION совпадают с первым SELECT, на странице рядом с товарами появятся логины и хеши паролей из таблицы users. Error-based вариант опирается на то, что приложение показывает текст ошибки SQL (имя таблицы, синтаксис) — по нему уточняют структуру БД перед UNION.

Слепая boolean-based SQLi

Иногда приложение не выводит строки БД и не показывает ошибки SQL, но по-прежнему подставляет ввод в запрос. Тогда атакующий задаёт вопросы вида "длина имени базы равна 6?" и смотрит на косвенный признак — есть ли товары в категории, редирект, код 200 vs 404, размер HTML.

Запрос каталога:

SELECT name, description FROM products WHERE category = 'Gifts';

Ввод в параметр category:

' AND LENGTH(DATABASE()) = 6 --
  • если длина имени текущей БД 6 — условие истинно, выборка как для обычной категории, страница "нормальная";
  • если длина другая — условие ложно, пустой результат, страница "пустая" или с другим шаблоном.

Перебирая длину, затем символы (SUBSTRING, ASCII), можно восстановить имя БД, таблицы и значения побитово, без единой утечки в HTML.

Слепая time-based SQLi

Когда ответ страницы не отличается визуально, используют задержку выполнения на стороне СУБД. Истинное условие вызывает SLEEP (MySQL), pg_sleep (PostgreSQL), WAITFOR DELAY (SQL Server) — атакующий измеряет время до полного ответа HTTP.

Пример для MySQL (проверка первой буквы имени БД):

' OR IF(SUBSTRING(DATABASE(), 1, 1) = 's', SLEEP(5), 0) --
  • буква s — ответ приходит примерно на 5 секунд позже;
  • другая буква — ответ быстрый.

Так подтверждают символы по одному. Метод медленный, но работает там, где boolean-слепая инъекция не даёт различимой реакции интерфейса.

Out-of-band

Данные уходят не в HTTP-ответ пользователю, а через отдельный канал — DNS-запрос, исходящий HTTP с хоста СУБД, почту. Нужны расширенные права в СУБД, разрешённый исходящий трафик и уязвимая функция (LOAD_FILE, xp_cmdshell, UTL_HTTP и т.п.). Встречается реже union- и blind-атак, но встречается в пентестах и CTF.

Связь с backend и SQL

Типы инъекций не зависят от языка сервера — важно, как строка запроса собирается перед отправкой в СУБД. Практика параметров — в главах SQLite, PostgreSQL; обзор уязвимостей приложения — Безопасность приложений.


Защита от SQLi

Один сценарий — два подхода (Python + SQLite):

# Уязвимо — конкатенация
cursor.execute(f"SELECT * FROM users WHERE login = '{user_input}'")

# Безопасно — параметры
cursor.execute("SELECT * FROM users WHERE login = ?", (user_input,))

Эффективная защита строится на нескольких уровнях:

  • Параметризованные запросы (Prepared Statements): гарантируют строгое разделение между кодом и данными. Значения передаются отдельно от текста запроса и интерпретируются исключительно как данные.
  • Экранирование специальных символов — менее надёжный подход, подвержен ошибкам и зависит от контекста (СУБД, кодировки).
  • Принцип наименьших привилегий — учётная запись приложения в СУБД должна иметь минимально необходимые права (без DROP, FILE, xp_cmdshell и т.п.).
  • Статический и динамический анализ кода: обнаружение потенциально уязвимых паттернов на этапе разработки и тестирования.

Инъекции команд операционной системы (Command Injection, CMDi)

Command Injection возникает, когда веб-приложение или программное обеспечение передаёт непроверенные пользовательские данные в системные команды (например, через System(), exec(), Process.Start() и аналоги).


Пример уязвимости


import os

os.system("ping -c 1 " + user_input)

Если user_input содержит 8.8.8.8; cat /etc/passwd, то будет выполнен ping и команда чтения файла паролей. Символы ;, &&, |, \n позволяют атакующему конструировать произвольные последовательности команд.


Контекст уязвимости

Особенно опасны такие инъекции на серверах с высокими привилегиями, где приложение запущено от имени root или SYSTEM. Последствия — полный контроль над сервером, установка бэкдоров, эксплуатация внутренней сети.


Защита

  • Избегать вызова системных команд на основе пользовательского ввода.
  • Если вызов необходим — использовать белый список допустимых значений.
  • Применять строгую валидацию и экранирование (но надёжнее — отказаться от динамического построения команд).
  • Запускать приложения в изолированных средах (контейнеры, chroot, sandbox).

Межсайтовый скриптинг (Cross-Site Scripting, XSS)

XSS — это форма инъекции, при которой вредоносный JavaScript-код внедряется в веб-страницу, отображаемую в браузере жертвы. В отличие от SQLi и CMDi, XSS эксплуатирует доверие пользователя к сайту.


Типы XSS

  1. Отражённый (Reflected): вредоносный скрипт передаётся через URL (например, параметр поиска) и сразу возвращается в ответе сервера. Атака требует, чтобы жертва перешла по специально подготовленной ссылке.
  2. Сохранённый (Stored) — скрипт сохраняется на сервере (в комментариях, профилях, форумах) и отдаётся всем пользователям, просматривающим заражённый контент.
  3. DOM-based: уязвимость возникает на стороне клиента — вредоносные данные модифицируют DOM-дерево без участия сервера (например, через location.hash, document.write).

Последствия

  • Кража куков сессии (включая HttpOnly в некоторых случаях).
  • Перенаправление на фишинговые страницы.
  • Логгирование нажатий клавиш (keylogging).
  • Выполнение действий от имени пользователя (например, перевод средств).

Защита

  • Экранирование контента — перед вставкой в HTML все специальные символы (<, >, ", ', &) должны быть заменены на HTML-сущности (&lt;, &gt; и т.д.).
  • Контекстно-зависимая обработка — правила экранирования различаются для HTML, атрибутов, JavaScript, CSS, URL.
  • Content Security Policy (CSP) — механизм, ограничивающий источники загрузки скриптов, стилей и других ресурсов.
  • Фреймворковые средства — современные фреймворки (React, Angular, Vue) по умолчанию экранируют вывод, но ручные манипуляции (innerHTML, dangerouslySetInnerHTML) могут обойти защиту.

Обобщённые принципы защиты от инъекций

Несмотря на различия в контекстах, все инъекции подчиняются общим принципам предотвращения:

  1. Разделение кода и данных: никогда не встраивайте пользовательские данные непосредственно в исполняемый код.
  2. Белые списки: вместо попыток заблокировать "плохие" символы — разрешайте только известные безопасные значения.
  3. Экранирование в соответствии с контекстом — если встраивание неизбежно, применяйте контекстно-зависимое экранирование, соответствующее целевой среде (SQL, OS, HTML и пр.).
  4. Минимизация поверхности атаки — отключайте ненужные функции (например, xp_cmdshell в MS SQL, exec в PHP), ограничивайте права, используйте песочницу (sandbox).
  5. Тестирование и мониторинг — регулярное проведение тестов на проникновение, статический анализ кода, логирование подозрительных запросов.

Редкие, но критичные формы инъекций

Помимо широко известных SQL-, командных и XSS-инъекций, существует ряд специализированных форм внедрения вредоносного кода, возникающих в узкоспециализированных средах — XML-процессоры, LDAP-каталоги, XPath-выражения, NoSQL-базы данных и др. Эти уязвимости часто остаются вне поля зрения разработчиков, поскольку подсистемы, в которых они возникают, считаются "внутренними" или "некритичными". Однако в современных распределённых архитектурах такие компоненты нередко становятся точками входа для атак.


XPath-инъекции

XPath (XML Path Language) — язык запросов к XML-документам, аналогичный SQL для реляционных баз. Если веб-приложение строит XPath-выражения на основе непроверенного пользовательского ввода, возможно внедрение произвольной логики.


Пример

Предположим, приложение ищет пользователя в XML-файле:

<users>
<user>
<login>admin</login>
<password>secret</password>
</user>
</users>

XPath-запрос может выглядеть так:

string xpath = "/users/user[login='" + login + "' and password='" + password + "']";

Если злоумышленник введёт в поле логина ' or '1'='1, то запрос примет вид:

/users/user[login='' or '1'='1' and password='...']

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


Защита

  • Использовать параметризованные XPath-выражения, если они поддерживаются (редко).
  • Применять строгую валидацию входных данных (белый список допустимых символов).
  • Избегать построения XPath-запросов на основе прямого пользовательского ввода.

LDAP-инъекции

LDAP (Lightweight Directory Access Protocol) — протокол для доступа к иерархическим каталогам (например, Active Directory). LDAP-инъекция возникает при некорректной обработке входных данных в поисковых фильтрах.


Пример

Фильтр может строиться так:

filter = "(uid=" + username + ")"

Если username содержит *)(uid=*))(|(uid=*, то фильтр превращается в:

(uid=*)(uid=*))(|(uid=*

Это может привести к обходу аутентификации или получению списка всех пользователей.


Особенности

LDAP использует собственный набор метасимволов — *, (, ), \, &, |, !. Их экранирование регламентировано в RFC 4515: каждый из этих символов должен быть заменён на \xx (шестнадцатеричное представление ASCII-кода).


Защита

  • Экранирование всех метасимволов в соответствии с RFC 4515.
  • Использование готовых библиотек для формирования фильтров (например, ldap.filter.escape в Python).
  • Применение строгой валидации (например, логин должен соответствовать регулярному выражению ^[a-zA-Z0-9_]+$).

NoSQL-инъекции

NoSQL-инъекции не являются прямым аналогом SQL-инъекций, поскольку большинство NoSQL-баз (MongoDB, CouchDB, Redis и др.) не используют строковые запросы в традиционном смысле. Вместо этого они принимают структурированные запросы — чаще всего в виде JSON-объектов или словарей. Уязвимость возникает при некорректной обработке пользовательских данных, которые интерпретируются как часть структуры запроса, а не как простые значения.


Пример (MongoDB)

Предположим, в приложении на Node.js происходит поиск по коллекции:

db.users.findOne({ username: req.body.username, password: req.body.password });

Если злоумышленник отправит JSON:

{
"username": { "$ne": "" },
"password": { "$ne": "" }
}

То MongoDB интерпретирует это как:

{ username: { $ne: "" }, password: { $ne: "" } }

Оператор $ne (not equal) означает "любое значение, кроме пустой строки". Таким образом, будет найден первый пользователь с непустыми логином и паролем — аутентификация обойдена.

В более сложных сценариях возможно использование операторов $where (выполнение JavaScript-кода на сервере MongoDB) или $regex для перебора данных побитно (аналог blind SQLi).


Защита

  • Строгая типизация входных данных: если ожидается строка — принимать только строки, отклоняя объекты.
  • Валидация структуры запроса — использование схем (например, Joi, Zod) для фильтрации и нормализации входных данных.
  • Отключение опасных операторов: в MongoDB можно запретить использование $where, $mapReduce и других функций на уровне базы.
  • Принцип наименьших привилегий: учётная запись приложения не должна иметь прав на выполнение административных команд.

Инъекции в динамические языки (eval-инъекции)

Многие скриптовые языки (JavaScript, Python, PHP, Ruby) поддерживают функции динамической интерпретации кода — eval(), exec(), Function(), import(), pickle.loads() и др. Использование таких функций с непроверенными данными представляет собой прямую инъекцию исполняемого кода.


Пример

user_input = request.GET['data']
result = eval(user_input)

Если user_input содержит __import__('os').System('rm -rf /'), последствия могут быть катастрофическими.


Особенности

Такие инъекции особенно опасны в средах с высокими привилегиями (например, административных панелях, внутренних инструментах). Часто разработчики считают, что "ввод контролируется", но забывают о возможностях сериализованных объектов (например, pickle в Python), которые при десериализации могут выполнять произвольный код.


Защита

  • Категорический отказ от eval() и аналогов в production-коде.
  • Использование безопасных альтернатив: JSON вместо eval для парсинга, шаблонизаторов вместо конкатенации кода.
  • При необходимости десериализации — использовать только доверенные форматы (JSON, XML с отключёнными DTD) и избегать собственных сериализованных объектов.
  • Изоляция среды выполнения (например, запуск в контейнере без доступа к файловой системе).

Объединяющий взгляд — модель "доверенной границы"

Все инъекции можно рассматривать через призму нарушения доверенной границы между данными и кодом. Приложение получает данные от внешнего агента (пользователя, API, файла) и передаёт их в интерпретатор (SQL, JS, OS shell и т.д.). Если граница между данными и кодом не защищена — интерпретатор не может отличить легитимные данные от вредоносных инструкций.

Фундаментальный принцип защиты — никогда не доверять внешним данным. Это означает, что:

  • Все входные данные должны считаться потенциально вредоносными до тех пор, пока не доказано обратное.
  • Построение исполняемой логики должно быть полностью отделено от передачи значений.
  • Контекст интерпретации должен быть максимально ограничен и изолирован.

Подробнее о проверках на границе приложения — Проверка и валидация.


Методы выявления и тестирования инъекций

Теоретическое понимание механизма инъекций необходимо, но недостаточно для обеспечения безопасности программных систем. Обнаружение уязвимостей требует системного подхода, сочетающего автоматизированные инструменты, ручной анализ и понимание контекста работы приложения.


1. Ручное тестирование (Manual testing)

Ручное тестирование остаётся наиболее надёжным методом выявления сложных и контекстно-зависимых инъекций. Оно предполагает:

  • Анализ всех точек ввода — формы и URL-параметры, HTTP-заголовки (User-Agent, Referer), cookie, тела запросов (включая JSON, XML), файлы, веб-сокеты.
  • Фаззинг входных данных: подстановка специальных последовательностей, характерных для разных типов инъекций:
    • Для SQLi — ', ", --, /*, UNION SELECT, SLEEP(), BENCHMARK().
    • Для CMDi: ;, &, |, $(...), `...`, %0a, &&, ||.
    • Для XSS — <script>, javascript:, onerror=, "><svg onload=...>.
    • Для NoSQL — { "$ne" — 1 }, { "$gt": "" }, { "$where": "..." }.
  • Наблюдение за поведением приложения — изменения в ответе, задержки (time-based blind), ошибки (error-based), перенаправления — всё это может быть индикатором уязвимости.
  • Использование прокси-инструментов — Burp Suite, OWASP ZAP позволяют перехватывать, модифицировать и повторно отправлять запросы, что критично для тестирования инъекций.

2. Автоматизированное сканирование

Автоматизированные сканеры уязвимостей (DAST — Dynamic Application Security Testing) могут быстро выявить классические инъекции, особенно отражённые формы XSS и SQLi. Однако они имеют ограничения:

  • Сложно обнаруживают слепые (blind) инъекции без чёткой сигнализации.
  • Часто дают ложные срабатывания (false positives) или пропускают уязвимости в нестандартных контекстах (например, GraphQL, WebSocket, SOAP).
  • Не понимают бизнес-логику приложения, что делает их беспомощными против логических уязвимостей.

Тем не менее, такие инструменты, как sqlmap (для SQLi), NoSQLMap (для NoSQL), dalfox (для XSS), являются мощными утилитами в руках специалиста и позволяют автоматизировать эксплуатацию подтверждённых уязвимостей.


3. Статический анализ кода (SAST)

SAST-инструменты анализируют исходный код или байт-код на наличие опасных паттернов:

  • Конкатенация строк с пользовательским вводом перед передачей в SqlCommand, os.system, eval, innerHTML.
  • Использование устаревших или небезопасных API.
  • Отсутствие вызовов функций экранирования.

Примеры инструментов:

  • SonarQube;
  • Checkmarx;
  • Semgrep;
  • Bandit (для Python);
  • ESLint с плагинами безопасности (для JS/TS).

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


4. Тестирование в CI/CD

Интеграция проверок на инъекции в процесс непрерывной интеграции позволяет выявлять регрессии и новые уязвимости до попадания кода в production. Это включает:

  • Запуск линтеров и SAST-сканеров при каждом коммите.
  • Автоматизированные unit- и интеграционные тесты с вредоносными входными данными.
  • Использование "загрязнённых" тестовых данных (taint analysis) для отслеживания путей передачи непроверенного ввода.

Стандарты и классификации

Инъекции официально признаны одной из самых критичных уязвимостей. В OWASP Top 10 инъекции (в широком смысле) неоднократно занимали первое место:

  • OWASP Top 10 2013, 2017: Injection (A1).
  • OWASP Top 10 2021 — хотя формально вытеснены более широкими категориями (например, "Software and Data Integrity Failures"), инъекции по-прежнему входят в основные векторы атак.

В системе CWE (Common Weakness Enumeration) инъекции представлены множеством записей:

  • CWE-79 — XSS,
  • CWE-89 — SQL Injection,
  • CWE-78 — OS Command Injection,
  • CWE-90 — LDAP Injection,
  • CWE-611 — XML External Entity (XXE), близкая по сути,
  • CWE-94 — Code Injection.

Эти идентификаторы используются в отчётах, стандартах сертификации (PCI DSS, ISO/IEC 27001) и системах управления уязвимостями.


Архитектурные подходы к предотвращению инъекций

Помимо кодовых практик, важно внедрять архитектурные меры:

  1. Zero Trust к входным данным: любые данные извне (включая внутренние микросервисы) рассматриваются как недоверенные.
  2. Изоляция компонентов — базы данных, файловые системы, командные оболочки должны быть недоступны напрямую из публичных интерфейсов.
  3. API-гейты и WAF: Web Application Firewall может блокировать известные сигнатуры инъекций, но не заменяет корректную реализацию. Лучше использовать его как дополнительный барьер.
  4. Политики выполнения: например, в Node.js можно использовать vm2 вместо eval, в .NET — AppDomain с ограниченными разрешениями (хотя в .NET Core/AppDomain упразднён, требуется другой подход — например, изоляция в контейнерах).

Основа по протоколу

Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.


Практика для командной разработки

Чтобы материал по инъекциям приносил эффект, полезно закрепить его в инженерных процессах.


Что добавить в процесс команды

  • Security-чеклист для каждого нового endpoint.
  • Набор негативных тестов на SQLi, XSS, CMDi и IDOR.
  • Статический анализ в CI для поиска опасных API (eval, innerHTML, shell-вызовы).
  • Обязательный код-ревью для мест, где формируются запросы к БД и внешним системам.

Мини-шаблон тест-кейсов

  1. Проверка строк с опасными символами (', ", <, >, ;, --).
  2. Проверка передачи объекта вместо строки там, где ожидается строка.
  3. Проверка длинных и нестандартных значений.
  4. Проверка повторного использования уязвимого payload в разных параметрах.

Частая ошибка

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


Связанные статьи