Я рад продолжить рассказывать свои взгляды к подходам создания безопасных веб-ресурсов и веб-приложений и перейти от первой части, которая содержит в себе некоторые общеполезные security-инструкции, ко второй — разработке самого приложения.
Программисты
Логично начать именно с них, с их знаний и опыта. Вина самого сотрудника в том, что он не обладает достаточными знаниями о security-разработке минимальна. Основная ответственность на руководстве, HR и ком-нибудь еще. Проблема возникает на этапе набора сотрудников. Рассмотрим типичное собеседование, даже основываясь на гайдах, которые выкладывались здесь, на Хабре:
- Собеседования на должность PHP разработчика — рейтинг 54
- Кандидаты с заявленным статусом «PHP-эксперт» и вопросы на логику — рейтинг 46, так же есть план собеседования
- Собеседование. Сегодня — рейтинг 68
- Идеальное собеседование? — рейтинг 71, обсуждение «идеального» собеседования
Никто нигде (в т.ч. последняя статья, в комментах) не упомянул ни разу о security-аспектах. «Рыба гниет с головы» и это факт. Что я предлагаю? При наборе сотрудников, во-первых, самому разобраться хотя в базовых моментах программирования с т.ч. зрения безопасности и позадавать по ним вопросы. И предлагаю следующие вопросы, ответы на которые (а главное — детальность объяснения) даст хоть какую-нибудь картину о знаниях разработчика в сфере безопасности. Выберем какие-нибудь типичные конструкции и уязвимости в них:
XSS
<?php
$name = $_GET['name'];
echo "Hello, <b>$name</b>!";
?>
Спрашиваем мнение. Неважно, знает ли соискатель reflected это, storage или DOM XSS. Главное, что он понимает, что подобные конструкции недопустимы (и будет круто, если знает последствия таких конструкций. А еще круче — решение). Задачу можно усложнить с подстановкой данных через JS, полученных откуда-либо.
SQLi
<?php
$value = "id = ".$_GET['id'];
$select->where($value);
?>
Это отсылка к одной из моих предыдущих статей (проблема не надумана, а была взята из практики). И так, мы ищем специалиста по Zend, он круто отвечает на все вопросы, но… Вполне нормально относится к подобным конструкциями. Или подставляет в where массив, пришедший из GET/POST через implode.
File Upload
<?php
if ($_FILES["file"]["type"] == "image/gif") || $_FILES["file"]["type"] == "image/jpeg" || $_FILES["file"]["type"] == "image/png") {
// Uploaded file is image, all ok
move_uploaded_file ...
}
?>
Загрузка картинки. Люди, имеющие хоть какой-нибудь опыт сраз скажут — что так уже никто не пишет. $_FILES[«file»][«type»] присваивается значение из HTTP, которое легко «подделывается». Ну да ладно, кандидат говорит, меняем просто на «file — ib» или используем PEAR/Zend для этого и дело с концом (при этом он ссылается на stackoverflow). Но и тут он будет не прав (но уже лучше). Довольно легко создается evil.php файл, содержащий php-код, при этом имеющий заголовки изображения. Так что тут нужно и проверять заголовки уже принятного файла (через `file`, PEAR или Zend, например), и задавать ему расширение, на основе опредленного mime-type.
Хочу сказать, что неверные (неточные) ответы не должны быть решающими при принятии сотрудника (это мнение основывается исходя из рациональных побуждений, а не с точки зрения безопасности), но имейте ввиду, что сотруднику нужно изучить определенный материал в этой области. И первое время нужно проверять его код, особенно в каких-то важных моментах (как загрузка файлов).
А еще у нас есть уже принятые сотрудники. С ними нужно вести отдельную работу и периодически «давать жару», разбирая какой-нибудь найденный уязвимый код при всех. Или хотя бы почтовой рассылкой с ссылками, что почитать на эту тему.
И так, переходим от человеческого фактора к техническому.
Иерархия
Начну со структуры каталогов. Она практикуется многими:
example.com
├── app
│ ├── config.ini
│ ├── framework
│ └── src
│ ├── controller
│ ├── model
│ └── view
├── crontab
│ ├── daily_flush_stat.php
│ └── weekly_remove_cache.sh
└── htdocs
├── css
├── img
├── index.php
└── js
Подобную структуру каталогов использую и я, и не вижу никаких проблем с ней. Начнем с последнего пункта — htdocs. В htdocs у нас всего 1 php скрипт — index.php (отсылка на первую часть статьи), эдакий bootsrtap, который стартует все приложение и отдает нужный функционал в зависимости от location. Там же лежит вся статика и upload от пользователей. Выполнимым к php остается всего 1 файл, и это замечательно!
app — application, вынесен за htdocs, это исключает в файлах модулей за ненадобностью подобные конструкции:
if (!defined('IN_SITE')) die ("Hack attempt!");
Прямой доступ к файлам в принципе невозможен. Так же нам не надо создавать кучу пустых index.html в каждой папке, если по каким-то неведомым причинам у нас включено индексирование в директориях и мы не можем это отключить.
config.ini так же не доступен из веба, соответственно не отдастся plain-text'ом по прямому обращению к нему.
Cron-скрипты (или скрипты для jenkins'a) так же удобно хранятся в нужной нам директории.
Код приложения
Разберем наиболее популярную схему использования шаблонов проектирования — MVC и решим, где у нас и что будет.
Model
Валидация. Многое зависит от того, что принято в вашей команде. Обычно, все же, судя выносят валидацию данных при инициализации модели. Но это может быть и с успехом сделано в другом месте (service как пример).
View
Здесь я рассмотрю извечный вопрос — где конвертировать спецсимволы HTML? Например, phpbb это делает при записи данных в базу и как итог — данные занимают большее место. Сообщение
WOW >>> "NEW NEW NEW"
займет не 42 байта (в UTF8), а 82, так как будет иметь вид
WOW >>> "NEW NEW NEW"
А так же может привести к ошибкам порядка валидации данных (которые и присутствуют у phpbb).
Моё мнение, что данные нужно записывать в том виде, в котором они пришли. И конвертировать спец. символы на выходе, в шаблонизаторе.
Конечно, непосредственная конвертация спец. символов при записи в базу является более безопасной, чем конвертирование их на выходе. Т.к. в одном месте разработчик все сделал правильно, а в другом месте — забыл (или выводил их другой разработчик). Но первый вариант можно считать костылем.
Controller
Основные уязвимости чаще всего встречаются именно в местах с манипуляцией данными. Здесь я хочу повториться о том, о чем писал еще в своих первых статьях, и предложить использовать следующую конструкцию:
try {
// try something
} catch (Exception $e) {
echo "Сожалеем, но произошла ошибка. Разработчики уже уведомлены и в скором времени все исправят!"
errorLog($e->getMessage());
}
Где errorLog() — добавит запись об ошибке в базу, вышлет письмо на dev@example.com или что-то еще. Вы всегда будете в курсе всех проблем, которые случаются на проекте. Так же, если мы используем New Relic (о котором было сказано в первой части статьи), то мы получим подобный красивый мониторинг:
Cкрин с примером логирования ошибки, когда кто-то пытался подобрать адрес phpmyadmin, например, при помощи использования nikto
Время, лог, и количество возникновения данной ошибки. error.log в современном варианте.
Итерации
Если ваш проект выпускается итерациями, то в подобную организацию процесса легко вписывается подход security-проверки выгружаемого кода на production. Делается полный diff и просматривается опытными программистами или security-отделом (человеком).
Framework, CMS/CMF
Разработка продукта на фреимворке — не панацея безопасности (особенно это подтвердил RoR с самого начала года). Использование самых популярных движков и полное использование их API при разработке модулей — так же панацея (WordPress => 3.5 XMLRPC pingback additional issues).
Заключение?
Мое мнение остается прежним, что лучше весь код писать с нуля, при этом уже имея опыт в безопасности (а лучше во взломе). Но, конечно, это практически неуместно в современном мире, так как это требует довольно больших дополнительных затрат для бизнеса (да и найти сразу столько специалистов довольно сложно).
Кстати, на эту тему у меня есть отличный график (о котором я узнал от Алексея Бабенко, который сам, в свою очередь, узнал о нем на одной из заграничных стажировок по безопасности)
Оптимальный уровень безопасности. Картинка найдена здесь
Из которого можно сделать выводы, что начальные вложения дают быстрый и резкий скачок в безопасности. Но чем выше будут вложения, тем все меньше будут уменьшаться риски.
Автор: BeLove