Работа над безопасностью любого приложения и особенно веб-приложения, с родни работы шпиона — об успехах не знает никто, о провалах слышат все.
Работа над безопасностью любого приложения и особенно веб-приложения, с родни работы шпиона — об успехах не знает никто, о провалах слышат все. Славное имя Вас, как разработчика, и клиента, как владельца бизнеса, может оказаться под угрозой благодаря одному забытому экранированию или пропущенной фильтрации. Заботясь о безопасности проектов, разрабатываемых на нашей платформе, мы ранее выпустили проактивный фильтр, который предотвращает эксплуатацию распространённых уязвимостей и в целом значительно подстраховывает разработчика от ошибок. Но его задача предотвратить эксплуатацию уязвимости, а не устранить её источник. Именно поэтому мы решили пойти немножечко дальше и начали работу над собственным инструментом для статического taint-анализа кода проекта (анализ приложения, производимый без его реального выполнения, рассчитанный на выявление ошибок связанных с некорректной обработкой пользовательских данных), который бы учитывал специфику разработки на нашей платформе.Главной нашей целью при решении данной задачи стало - дать удобный, точный и понятный инструмент для разработчика (да-да, именно для разработчика), который мог бы подсказать узкие места в безопасности его кода. Два основных тезиса были перед глазами на всей стадии разработки:
- Разработчик не пентестер, у него зачастую нет необходимой сноровки, необходимого образа мышления и опыта в данной области, поэтому ему порой достаточно сложно определить критичность потенциального огреха (тем более, если проект достается по «наследству»)
- «Точность обнаружения важнее полноты анализа» - люди склонны уставать и ускорятся, листая «километровые» отчеты в поисках заветной ошибки, а клиенты при виде длинного списка будут считать разработчика не благонадежным (коим он разумеется не является
)
Этим постом хочу немного приоткрыть завесу и рассказать об этом тесте, найти и опробовать который вы сможете не раньше версии 11.5.2 Главного модуля. Для того что бы найти его, необходимо будет проделать следующий путь: Администрирование -> Настройки -> Инструменты -> "Монитор качества" и выбрать наш новый тест “Предприняты меры по обеспечению безопасности проекта на уровне веб-разработки” в разделе “Безопасность”.
Запустив его, вы сможете просмотреть подробный отчет о его работе (при условии наличия найденных ним проблем):
От слов к делу или «лучше один раз увидеть...»
Как работает на практике тест и какой отчет он составляет, я покажу на примере уязвимого к Cross-site scripting компонента «mega_comp».
И так, сам компонент:
Файл: /bitrix/components/my_comps/mega_comp/component.php
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
$my_sec_var=htmlspecialchars($_REQUEST['sec_var']);
$my_unsec_var=$_REQUEST['unsec_var'];
$arResult['sec_var']=$my_sec_var;
$arResult['unsec_var']=$my_unsec_var;
$this->IncludeComponentTemplate();
?>
|
Модификация результатов перед выводом:
Файл: /bitrix/templates/minimal_black/components/my_comps/mega_comp/.default/result_modifier.php
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
$arResult['~sec_var']=htmlspecialcharsback($arResult['sec_var']);
?>
|
Сам шаблон компонента:
Файл: /bitrix/templates/minimal_black/components/my_comps/mega_comp/.default/template.php
<?if(!defined("B_PROLOG_INCLUDED") || B_PROLOG_INCLUDED!==true) die();
//безопасные данные
echo $arResult['sec_var'];
//данные которые стали не безопасны в процессе работы скрипта
echo $arResult['~sec_var'];
//изначально небезопасные данные, выводятся с условием
if (isset($arResult['unsec_var']))
echo $arResult['unsec_var'];
?>
|
Файл: /test.php
<?require($_SERVER["DOCUMENT_ROOT"]."/bitrix/header.php");
$APPLICATION->IncludeComponent('my_comps:mega_comp','',Array());
require($_SERVER["DOCUMENT_ROOT"]."/bitrix/footer.php");?>
|
Как видно из скриншота он нашел две XSSки, которые выводятся в шаблоне как $arResult['~sec_var'] и $arResult['unsec_var'], определив при этом что переменная $arResult['sec_var'] полностью безопасна. Показал полный путь следования переменных от начала инициализации пользовательскими данными до попадания их в вывод (указав слева номера строк, а справа файл содержащий эту конструкцию). Не забыв при этом указать, что для вывода переменной $arResult['unsec_var'] должно быть соблюдено определенное условие.
На текущий момент он нацелен искать ошибки, которые могут привести к:
- XSS (Cross-site scripting) нападению
- SQL инъекции
- Выполнению произвольного php кода
- Выполнение произвольных системных комманд
- Инъекции в заголовок ответа (HTTP Response Splitting)
- File Inclusion
А теперь кратенько о самой концепции
Статический taint-анализ (или taint checking) решает задачу поиска уязвимых мест в лоб, и в основе лежит всего лишь несколько правил:
- Данные, полученные скриптом из http-запросов, считаются небезопасными по определению
- Небезопасные данные в ходе работы скрипта могут стать безопасными (приведение типов, фильтрация, кодирование, нормализация и т.д.)
- Безопасные данные, содержащие пользовательские данные, могут стать небезопасными (декодирование, htmlspecialcharsback и т.д.)
- Небезопасные данные не должны попадать в контролируемые функции (вывод, системные функции, запрос к СУБД и т.д.)
- За небезопасные данные принимаются только данные, полученные из http-запроса, т.е. не учитываются случаи, когда они могут прийти, например, из СУБД или хранится в файле, что разумеется снижает полноту анализа.
- При статическом анализе достаточно сложно рулить ветвлением логики и, как следствие, в отчет попадают опасные участки кода, которые на практике не станут уязвимостями.
Первое ограничение, на самом деле, можно обойти с достаточной долей успеха, и мы постараемся сделать это в будущем, сейчас же это умышленно не реализовывалось в виду слишком большого количества ложно положительных результатов. Например, имеем гостевую книгу, разделенную на два скрипта. Один пишет в СУБД данные полученные из формы, второй – получает данные из СУБД и выводит их пользователю:
//Скрипт add_posts.php
$text = addslashes(htmlspecialchars($_POST['text']));
$name = addslashes(htmlspecialchars($_POST['name']));
mysql_query("INSERT INTO posts (name, text) VALUES ('".$name."','".$text."')");
//Скрипт view_posts.php
$result = mysql_query("SELECT name, text FROM posts");
while ($row = mysql_fetch_assoc($result)) {
echo 'Имя:'.$row['name'].'<br />Сообщение:'.$row['text'].'<br />';
|
Второе ограничение, увы можно решить лишь частично. Приведу пример:
if (in_array($_GET['my_var'],explode(" ",COption::GetOptionString('my_module','vars'))))
echo $_GET['my_var'];
|
if(!check_bitrix_sessid())
{
//чёт там происходит
die();
}
if (!in_array($_GET['var'],$some_vars))
exit('Не допустимый аргумент:'.$_GET['var']);
|
Заключение
Идей у нас в запасе еще достаточно много, поэтому тест будет активно развиваться и совершенствоваться. Главное стоит понять - статические анализаторы реально ищут не уязвимости, а потенциальные ошибки, которые могут стать (а могут и не стать) уязвимостями, решить уязвимо ли это место в состоянии только разработчик. Так же не стоит забывать о том, что любые средства по анализу кода никогда не заменят Code Review, «думанье головой» и периодический аудит безопасности.
Если у вас есть какие либо вопросы, спрашивайте пожалуйста.
Фото: