- PVSM.RU - https://www.pvsm.ru -

На пути к созданию безопасного веб-ресурса. Часть 2 — разработка

Я рад продолжить рассказывать свои взгляды к подходам создания безопасных веб-ресурсов и веб-приложений и перейти от первой части [1], которая содержит в себе некоторые общеполезные security-инструкции, ко второй — разработке самого приложения.

Программисты

Логично начать именно с них, с их знаний и опыта. Вина самого сотрудника в том, что он не обладает достаточными знаниями о security-разработке минимальна. Основная ответственность на руководстве, HR и ком-нибудь еще. Проблема возникает на этапе набора сотрудников. Рассмотрим типичное собеседование, даже основываясь на гайдах, которые выкладывались здесь, на Хабре:

Никто нигде (в т.ч. последняя статья, в комментах) не упомянул ни разу о 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);
?>

Это отсылка к одной из моих предыдущих статей [6] (проблема не надумана, а была взята из практики). И так, мы ищем специалиста по 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 [7]). Но и тут он будет не прав (но уже лучше). Довольно легко создается 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 &gt;&gt;&gt; &quot;NEW NEW NEW&quot;

А так же может привести к ошибкам порядка валидации данных (которые и присутствуют у phpbb).
Моё мнение, что данные нужно записывать в том виде, в котором они пришли. И конвертировать спец. символы на выходе, в шаблонизаторе.
Конечно, непосредственная конвертация спец. символов при записи в базу является более безопасной, чем конвертирование их на выходе. Т.к. в одном месте разработчик все сделал правильно, а в другом месте — забыл (или выводил их другой разработчик). Но первый вариант можно считать костылем.

Controller

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

try {
    // try something
} catch (Exception $e) {
    echo "Сожалеем, но произошла ошибка. Разработчики уже уведомлены и в скором времени все исправят!"
    errorLog($e->getMessage());
}

Где errorLog() — добавит запись об ошибке в базу, вышлет письмо на dev@example.com или что-то еще. Вы всегда будете в курсе всех проблем, которые случаются на проекте. Так же, если мы используем New Relic (о котором было сказано в первой части статьи), то мы получим подобный красивый мониторинг:

На пути к созданию безопасного веб ресурса. Часть 2 — разработка [8]
Cкрин с примером логирования ошибки, когда кто-то пытался подобрать адрес phpmyadmin, например, при помощи использования nikto [9]

Время, лог, и количество возникновения данной ошибки. error.log в современном варианте.

Итерации

Если ваш проект выпускается итерациями, то в подобную организацию процесса легко вписывается подход security-проверки выгружаемого кода на production. Делается полный diff и просматривается опытными программистами или security-отделом (человеком).

Framework, CMS/CMF

Разработка продукта на фреимворке — не панацея безопасности (особенно это подтвердил RoR с самого начала года). Использование самых популярных движков и полное использование их API при разработке модулей — так же панацея (WordPress => 3.5 XMLRPC pingback additional issues [10]).

Заключение?

Мое мнение остается прежним, что лучше весь код писать с нуля, при этом уже имея опыт в безопасности (а лучше во взломе). Но, конечно, это практически неуместно в современном мире, так как это требует довольно больших дополнительных затрат для бизнеса (да и найти сразу столько специалистов довольно сложно).
Кстати, на эту тему у меня есть отличный график (о котором я узнал от Алексея Бабенко, который сам, в свою очередь, узнал о нем на одной из заграничных стажировок по безопасности)

На пути к созданию безопасного веб ресурса. Часть 2 — разработка
Оптимальный уровень безопасности. Картинка найдена здесь [11]

Из которого можно сделать выводы, что начальные вложения дают быстрый и резкий скачок в безопасности. Но чем выше будут вложения, тем все меньше будут уменьшаться риски.

Автор: BeLove

Источник [12]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/bezopasnost/27657

Ссылки в тексте:

[1] первой части: http://habrahabr.ru/post/168739/

[2] Собеседования на должность PHP разработчика: http://habrahabr.ru/post/21681/

[3] Кандидаты с заявленным статусом «PHP-эксперт» и вопросы на логику: http://habrahabr.ru/post/148509/

[4] Собеседование. Сегодня: http://habrahabr.ru/post/67963/

[5] Идеальное собеседование?: http://habrahabr.ru/post/120642/

[6] моих предыдущих статей: http://habrahabr.ru/post/140145/

[7] stackoverflow: http://stackoverflow.com/questions/652002/detecting-mime-type-in-php

[8] Image: http://cs309918.userapi.com/v309918003/41f2/foQ4rMw9Wzo.jpg

[9] nikto: http://en.wikipedia.org/wiki/Nikto_Web_Scanner

[10] WordPress => 3.5 XMLRPC pingback additional issues: http://lab.onsec.ru/2013/01/wordpress-xmlrpc-pingback-additional.html

[11] здесь: http://www.musc.edu/security/guidelines/MUSC-Risk-Management-Guidelines.html

[12] Источник: http://habrahabr.ru/post/168823/