Безопасное программирование или как не стать жертвой

Stass

Проверка на авторские статьи.
Главный редактор
Статус
Offline
С нами
Сообщения
169
Симпатии
6
Розыгрыши
0
#1
О статье

В этой статье я рассказываю о некоторых практиках создания безопасного программного обеспечения.

Первая ее часть посвящена безопасному программированию.

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

С другой стороны, применяют их не очень часто. Для многих программистов и менеджеров проекта они остаются не очень понятной экзотикой.

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

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

Внимание! Статья большая и подразумевает внимательное прочтение.

». Как следует из названия, проект посвящен безопасности веб приложений, однако эту же информацию можно применять и при программировании в других областях. Сайт сделан в популярной сейчас форме википедии, и состоит из отдельных статей; есть списки этих статей, сгруппированные по разным принципам. Указатель на статьи, сгруппированные по технологиям, можно найти по следующему .

Я и дальше буду неоднократно ссылаться на OWASP. Этот проект — очень ценный ресурс для специалистов, занимающихся безопасностью приложений.

Очень хорошим источником информации может послужить сайт . Сайт содержит интересную нам информацию в виде каталога, в котором перечислены причины возникновения уязвимостей в программном обеспечении. Воспринимается он сложнее, чем OWASP, но может оказаться более удобным, например, при формировании списков проверки.

Сверяйтесь с этими сайтами регулярно. Информация о безопасности технологий постоянно обновляется, здесь как в зазеркалье: чтобы оставаться на месте, надо бежать со всех ног.

Используйте библиотеки

Не изобретайте велосипед! Конечно, всем нам очень интересно сделать что-то свое. Но в безопасности изобретательство приводит к очень неприятным последствиям, поэтому используйте проверенные средства.

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

Например, для реализации криптографических методов можно рекомендовать . Библиотека хорошо известна, проверена в достаточной для большинства из нас степени, распространяется под свободной .

Веб программистам, использующим Java EE, может понравиться . Эта библиотека создана в рамках уже упоминавшегося здесь проекта OWASP, она реализует множество методов, необходимых для создания безопасных веб приложений. В библиотеке присутствуют функции, осуществляющие фильтрацию входных данных, аутентификацию пользователя, проверку прав доступа, и многое другое. Код лицензирован под BSD лицензией, допускающей очень широкое использование.

К сожалению, ESAPI сейчас не развивается. Тем не менее, это хорошо зарекомендовавшая себя библиотека, и есть надеяться, что ее поддержка будет возобновлена.

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

Это только некоторые возможности. Я упомянул их исключительно в качестве примера, список библиотек далеко не ограничен только ими. Более того, многие современные фреймворки также содержат необходимые функции. Обращайте только внимание, кто разрабатывал эту библиотеку, насколько хорошо она проверена и протестирована.

Иногда в проекте невозможно использование чужих библиотек. В крайнем случае (совсем-совсем крайнем!) напишите свою, тщательно ее проверьте и протестируйте, попросите специалистов проверить и протестировать, еще раз сами тщательно проверьте и протестируйте. Используйте функции из этой библиотеки в своем коде, не пишите новые методы, каждый раз, когда они вам нужны.

В безопасности вообще так: меньше велосипедов — лучше решение.

Делайте самопроверку

Мы все торопимся. Код, который мы пишем сейчас, надо было сдать еще вчера, поэтому единственное необходимое для него качество — он должен компилиться. Ну, может быть, он еще должен делать что-то полезное, проходить какой-то набор тестов.

Остановитесь. Когда вы только написали свой код, еще до его компиляции, сделайте небольшой перерыв, отдохните, забудьте про код. После перерыва посмотрите на написанное с разных сторон: проверьте синтаксис, соответствие кода корпоративному стандарту; проверьте логику кода; проверьте его соответствие требованиям безопасности («знайте свои технологии», помните?).

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

Самопроверка является признанной практикой создания качественного ПО. Так она включена в (Personal Software Process), процесс, разработанный специально для создания качественного продукта в условиях жестких временны́х и финансовых ограничений.

Обратите внимание: суета — враг не только качества, но и быстроты.

Используйте утилиты проверки кода

Человеку свойственно делать глупые ошибки. При этом все программисты знают, что именно глупые ошибки труднее всего обнаружить: какая-нибудь непоставленная точка с запятой может привести к потере целого дня (если не больше).

Человеку сложно помнить все сразу. Для уязвимостей существует огромное количество причин; чтобы избежать проблем, надо помнить их все, надо проверять программу на их наличие. Более того, взломщики не стоят на месте: то, что считалось безопасным вчера, сегодня уже может быть уязвимо.

Частично уменьшить проблемы с глупыми ошибками и быстро меняющимся миром могут . Эти программы просматривают код и ищут в нем признаки возможных проблем с безопасностью. Автоматизированные утилиты хороши тем, что могут быстро и почти без участия человека находить многие типы уязвимостей, давая возможность программисту обращать больше внимания на другие проблемы. Зачастую, эти утилиты могут проводить достаточно сложный анализ, который у человека занял бы очень много времени. Некоторые из этих возможностей уже встроены в современные компиляторы, надо только знать, как их включить.

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

Автоматизация может освободить наше время для чего-то более интересного. Если часть своей работы можно поручить компьютеру, то почему бы это не сделать?

Тестируйте

Чтобы быть безопасной, программа должна демонстрировать определенное поведение. Например, при попытке ввода слишком длинной строки программа должна ее либо отвергать, либо обрезать; при попытке ввода специальных символов данные должны либо отвергаться, либо символы должны специальным образом кодироваться.

Такое поведение может быть протестировано. И мы получаем все преимущества автоматизированного тестирования программы: его можно проводить часто, практически без участия человека.

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

Не все может быть протестировано эффективно. Например, использование функции gets() почти всегда приводит к переполнению буфера; эта проблема может быть обнаружена при тестировании. Но более эффективно обнаруживать ее с помощью автоматизированного сканирования кода: простой grep легко с этим справляется. Пример, конечно, сильно утрирован, но может являться хорошей отправной точкой для обдумывания возможностей тестирования безопасности.

Тестирование не может решить всех проблем безопасности. Но если что-то возможно протестировать автоматически, почему этим не пользоваться?

Делайте code review

Ревизия кода — самый эффективный способ обнаружения ошибок. Причем он наиболее эффективен как с точки зрения количества обнаруживаемых дефектов, так и с точки зрения стоимости (времени) их обнаружения. Так Стив Макконнел в своей книге «Совершенный код» приводит ссылку на исследование компании IBM, в котором обнаружили, что один час, инвестированный в ревизию кода, сохраняет до 100 часов тестирования и устранения ошибок. Хотя я думаю, что число 100 будет достигаться очень редко, тем не менее, это очень хороший повод задуматься.

Ревизию кода можно организовать по-разному. В этой роли может выступать и парное программирование, и неформальный просмотр кода коллегами-программистами, и очень формальная инспекция с привлечением специалиста по безопасному программированию. Многое зависит от поставленных целей: чем более высокие требования предъявляются к программе, тем большее количество людей должны быть вовлечены в ревизию, тем более формально она должна проводиться.

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

И опять OWASP. Конечно, этот проект не мог обойти такую важную методологию стороной. Поэтому, если вы собираетесь проводить ревизию кода в своем проекте (я вас еще не убедил это делать?) соответствующая этого сайта — хорошая точка для старта.

Так вы еще не делаете code review? Нет вам прощения!!!

Используйте все меры в комплексе

Э… Здесь что-то надо писать?

Часть II. Проектирование безопасности

Мотивирующая аналогия

Давайте пока отвлечемся от безопасности. Одной из самых часто встречающихся программистам задач является изменение уже существующей программы. Нам не очень часто доводится создавать программу «с нуля», а вот изменять («поддерживать») уже существующую — сколько угодно.

Итак, нам предстоит изменить существующую программу. И пусть это будет «существенное» изменение, чтобы это не значило, каждый программист легко может сам додумать ситуацию.

Есть одна проблема. Программа при своей начальной разработке не была предназначена для изменений. Не знаю почему: то ли не подумали, то ли очень спешили — не важно, есть результат. Программа состоит из большого количества модулей, сильно зависимых друг от друга. Делая небольшие изменения в одном модуле, мы ломаем другие. Знакомо?

Серьезная ли это проблема?

Пусть у нас есть программа в 100 строк. Думаю, каждый более или менее квалифицированный программист легко внесет в нее любое, даже очень «существенное», изменение. И займет у него это, если не день, то уж не больше двух-трех, здесь смешно даже обсуждать «изменение».

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

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

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

Поэтому все мы знаем про SOLID и, возможно, про другие подобные принципы. Если программа с самого начала проектировалась и развивалась с учетом этих принципов, то поддерживать ее становится намного проще. Для очень больших программ проектирование с учетом дальнейшей поддержки вообще становится необходимым условием ее возможности.

А теперь вернемся к безопасности.

Принципы безопасных архитектуры и дизайна

В безопасности есть свой «solid». Не думаю, что это заявление кого-то из вас удивит: про существование этих принципов все (или почти все) знают, на них регулярно ссылаются в обсуждениях на этом сайте.

Давайте я напомню эти принципы:

  • простота механизмов (economy of mechanism);
  • безопасность по умолчанию (fail-safe defaults);
  • полное проникновение защиты (complete mediation);
  • открытый дизайн (open design);
  • разделение полномочий (separation of privilege);
  • минимум привилегий (least privilege);
  • минимизация разделения ресурсов (least common mechanism);
  • психологическая приемлемость (psychological acceptability).

Эти принципы были сформулированы почти 40 лет назад в статье “ ”. Сейчас к ним любят добавлять еще несколько правил ( ), но факт тот, что эти принципы известны уже давно, и они остаются верными до настоящего времени. Есть и примеры удачных решений, построенных в соответствии с этими принципами.

Казалось бы, проектируй себе на здоровье, людям на безопасность.

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

Мы должны использовать шаблоны.

Шаблоны безопасных архитектуры и дизайна

В нашей работе постоянно возникают повторяющиеся задачи. Каждая система, каждая программа в чем-то похожа на многие другие. И для стандартных задач существуют стандартные решения — шаблоны.

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

В программировании хорошо известны design patterns. Это шаблонные решения задач, возникающих при проектировании программ. Их использование существенно упрощает нам жизнь: надо привести стоящую перед нами задачу к стандартной, и применить уже имеющееся, заведомо работающее решение. Поэтому книга «банды четырех» — практически обязательное чтение для любого программиста.

Шаблонами проектирование дело не ограничивается: шаблоны существуют на всех уровнях. Есть шаблоны архитектуры (вспомним, хотя бы MVC), есть шаблоны написания кода, есть шаблоны пользовательского интерфейса, есть шаблоны поведения программы. Практически на каждую возникающую задачу есть свои шаблонные решения.

В безопасности также имеются свои шаблоны. Шаблонные решения есть для всех уровней построения информационной системы. Существуют шаблоны безопасности предприятия в целом, включающие организационные меры защиты; существуют шаблоны построения информационной системы, как чисто технической сущности; и существуют шаблоны безопасных приложений.

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

Безопасность программы часто связывается именно с ее безопасным поведением. Оно включает в себя, например аутентификацию пользователя, проверку прав его доступа, фильтрацию входных данных. Поведение — это то, что легко можно показать, протестировать; именно поэтому маркетологи обычно «продают» исключительно функции безопасности.

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

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

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

Первая публикация — «Security Design Patterns» ( ). На мой взгляд, это одна из самых качественных публикаций, посвященных структурным паттернам безопасных программ. Она была опубликована почти 10 лет назад, в 2004 году консорциумом . В этой публикации вы найдете как методику проектирования безопасности с использованием шаблонов, так и описание многих из них.

Например, очень интересно описание шаблона «Protected System». В других источниках он более известен, как «Reference Monitor», и очень часто упоминается в литературе. Но, чаще всего, именно упоминается: в большинстве других публикаций ему посвящается от силы полстраницы. В работе Open Group использование шаблона «Protected System» разбирается очень подробно, в том числе продемонстрированы возможные варианты его использования.

Вторая публикация — «Secure Design Patterns» ( ). Это более свежая информация, отчет о работе, проделанной в и спонсированной Министерством обороны США.

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

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

Читайте, думайте, применяйте.

Заключение

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

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

Качественная архитектура и дизайн сложной программы — единственный способ сделать ее безопасной.

Вспомните об этом, когда следующий раз будете планировать новую программу или рефакторинг уже существующей.

P.S. Статья получилась большой. Я не стал делить ее на части, очень хотелось сохранить ее целостность. Надеюсь, было интересно, и я не слишком вас загрузил.