На пути к безопасности, новый тест штатного чеклиста

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

Работа над безопасностью любого приложения и особенно веб-приложения, с родни работы  шпиона — об успехах не знает никто, о провалах слышат все. Славное имя Вас, как разработчика, и клиента, как владельца бизнеса, может оказаться под угрозой благодаря одному забытому экранированию  или пропущенной фильтрации. Заботясь о безопасности проектов, разрабатываемых на нашей платформе, мы ранее выпустили проактивный фильтр, который предотвращает эксплуатацию распространённых уязвимостей и в целом значительно подстраховывает разработчика от ошибок. Но его задача предотвратить эксплуатацию уязвимости, а не устранить её источник. Именно поэтому мы решили пойти немножечко дальше и начали работу над собственным инструментом для статического 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();
?>
Из листинга видно, что в $arResult['sec_var'] попадает безопасная переменная, а в $arResult['unsec_var'] – наоборот.
Модификация результатов перед выводом:
Файл: /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']);
?>
В $arResult['~var'] теперь хранятся ранее безопасные данные, которые благодаря htmlspecialcharsback стали небезопасны, т.к. содержат пользовательские данные.
Сам шаблон компонента:
Файл: /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 и т.д.)
  • Небезопасные данные не должны попадать в контролируемые функции (вывод, системные функции, запрос к СУБД и т.д.)
Таким образом, все что нам требуется — проследить путь переменной от инициализации пользовательскими данными до попадания во входящий параметр контролируемой нами функции, расставив при этом необходимые метки, говорящие о безопасности отслеживаемой переменной. Причина, по которой мы реализовали наш тест, основываясь на этой модели, далеко не одна: скорость работы, удобство эксплуатации и главное — настраиваемая точность. Увы у этой модели присутствует целый ряд ограничений, основных два:
  1. За небезопасные данные принимаются только данные, полученные из http-запроса, т.е. не учитываются случаи, когда они могут прийти, например, из СУБД или хранится в файле, что разумеется снижает полноту анализа.
  2. При статическом анализе достаточно сложно рулить ветвлением логики и, как следствие, в отчет попадают опасные участки кода, которые на практике не станут уязвимостями.

Первое ограничение, на самом деле, можно обойти с достаточной долей успеха, и мы постараемся сделать это в будущем, сейчас же это умышленно не реализовывалось в виду слишком большого количества ложно положительных результатов. Например, имеем гостевую книгу, разделенную на два скрипта. Один пишет в СУБД данные полученные из формы, второй – получает данные из СУБД и выводит их пользователю:
//Скрипт 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 />';
      Как видно из листинга, если в анализ включить данные полученные из СУБД, то он с полной уверенностью скажет, что в скрипте view_posts.php у нас хранимая XSS, и будет абсолютно прав, хотя по факту данные конечно же правильно отфильтрованы (пусть идеологически и не правильно хранятся, но мы же сейчас говорим о безопасности:) ), и ни о какой хранимой XSS и речи быть не может. Это происходит в виду отсутствия связи между скриптами add_posts.php и view_posts.php при анализе.

Второе ограничение, увы можно решить лишь частично. Приведу пример:
if (in_array($_GET['my_var'],explode(" ",COption::GetOptionString('my_module','vars'))))
   echo $_GET['my_var'];
      В данном случае при статическом анализе определить, что будет находится в COption::GetOptionString('my_module','vars') при выполнении скрипта не представляется возможным, а главное установить безопасность этих данных. Но не все так плохо, как может показаться, часть проблем вполне решаема, например, уже сейчас он знает что если в коде ему встретится примерно такой код:
if(!check_bitrix_sessid())
{
   //чёт там происходит
   die();
}

if (!in_array($_GET['var'],$some_vars))
   exit('Не допустимый аргумент:'.$_GET['var']);
то в месте вывода $_GET['var'] нет XSS уязвимости, т.к. для её эксплуатации необходим токен сессии жертвы, получить который злоумышленник не в состоянии. Правильная обработка ветвления логики одна из самых сложных, но в то же время и приоритетных задач для нас при анализе. 

      Заключение
Идей у нас в запасе еще достаточно много, поэтому тест будет активно развиваться и совершенствоваться. Главное стоит понять - статические анализаторы реально ищут не уязвимости, а потенциальные ошибки, которые могут стать (а могут и не стать) уязвимостями, решить уязвимо ли это место в состоянии только разработчик. Так же не стоит забывать о том, что любые средства по анализу кода никогда не заменят Code Review, «думанье головой» и периодический аудит безопасности.

Если у вас есть какие либо вопросы, спрашивайте пожалуйста.
Фото:

Мы аккуратно собираем действительно полезные материалы для собственников интернет-магазинов и интернет-маркетологов, касающиеся разработки и эксплуатации быстро масштабируемых e-commerce проектов.

Мы - рядом

У Вас есть проект? Давайте его обсудим!

Приедем на встречу

г.Ростов-на-Дону, ул.Социалистическая, 74

г.Москва, ул.Люблинская, 42, офис 330

Пишите на email

info@orangecode.ru

Контактный телефон

+7 (863) 308-15-70

Skype

web_studio_orange_code

Viber

+7 (918) 505 23 85

WhatsApp

+7 (918) 505 23 85

Оставьте заявку

Расскажите немного о Вашем проекте. Мы обязательно свяжемся с Вами и сделаем коммерческое предложение, от которого Вы не сможете отказаться!

Я согласен на обработку моих персональных данных в соответствии с Политикой конфиденциальности

Настоящим я выражаю согласие на обработку моих персональных данных, включая передачу третьим лицам, уполномоченным Orange Code для осуществления целей маркетинга, рекламы и изучения мнений группой компаний Orange Code. Я прочитал Политику Конфиденциальности и согласен с ее положениями. Я понимаю, что могу отозвать свое согласие, следуя по специальной ссылке.