пятница, 15 июля 2016 г.

Защита Программного Обеспечения: правила написания безопасного кода

Уязвимости системного и прикладного Программного Обеспечения (ПО) стабильно и уже долгое время занимают лидирующие строки в рейтинге кибер-угроз. Не смотря на совершенствование инструментов разработки, созданию новых подходов к проектированию и программированию, непрерывно растет сложность задач, а следовательно и написанного кода. Это в свою очередь ведет к увеличению риска появления критических ошибок в ПО. Для обеспечения безопасности кода пользуются современные технологии проверки - статический и динамический анализ, ручная инспекция, Fuzz testing и другие. Но сегодня в публикации речь пойдет не об инструментальных средствах, а о концепции проектирования и написания  безопасного кода, которые мы собрали из различных источников и собственного опыта.

В нынешнее время весьма хорошим результатом для опытного программиста считается 1 серьёзная ошибка на 1000 строк кода. Для примера, в новомодной Windows 8 содержится 7 миллионов строк. В таком случае, количественно потенциальных критических ошибок подсчитать сможет даже ученик начальных классов.

В помощь программистам для выявления ошибок кода в ПО созданы специальны инструменты - статические и динамические анализаторы кода, а так же зарекомендовавший себя ручной анализ, получивший название инспекция кода (code inspection). Дополнительными инструментами можно считать системы управления версиями и ветками релизов, например такие как SVN и Git. И система отслеживания ошибок (bug tracking system)

Если кратко, то все ошибки (баги) можно разделить на несколько видов:
  • Борбаг — легко обнаруживаемый стабильный баг
  • Гейзенбаг — сложно обнаруживаемый, периодически исчезающий и меняющий свойства баг при попытке его обнаружения
  • Мандельбаг — баг с очень сложным, хаотичным, поведением
  • Шрёдинбаг — критическая ошибка, которая не проявляется пока кто-нибудь на неё не наткнется в исходном коде, после чего программа совершенно перестает работать

Если тема тестирования ПО стала интересна, том могу предложить продолжить чтение на эту тему  статьи на Хабре

А вот в этой статье можно больше узнать о  этимология и энтомология Багов.

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

Топ 10 правил безопасного написания кода от CERT
  1. Проверяйте входящие данные. 
  2. Учитывайте предупреждения компилятора. 
  3. Проектируйте с возможностью разделения привелегий. 
  4. Делайте все простым. 
  5. По умолчанию запрещайте. 
  6. Используйте наименее возможные привелегии. 
  7. Уберите лишюю информацию при отправке в сторонние системы. 
  8. Защищайтесь на всех уровнях. 
  9. Используйте хорошие системы контроля качества. 
  10. Разработайте правила/стандарт безопасного написания кода. 

Подробней можно прочесть на странице CERT (на английском).

Общие принципы проектирования защищённых приложений
  1. Защита должна ставиться, как неотъемлемая функция создаваемого ПО.
  2. На обеспечение безопасности ПО должно быть отведено достаточно времени.
  3. Обязательно должна быть составлена модель угроз: декомпозиция ПО с выявлением присущих уязвимостей; определение степени опасности и вероятности возникновения каждой уязвимости/опасности; составление матрицы угроз; определение противодействий, а также действий в случае реализации угрозы.
  4. Определение процедуры удаления небезопасных функций и частей в ПО.
  5. Должны быть созданы метрики безопасности, соответствующие модели угроз, в которых должны быть определены предельные пороги.
  6. Разработка тест-планов и периодическая проверка и контроль процесса создания ПО отделом ИБ на каждом этапе разработки по созданным тест-планам, по возможности, - приглашёнными специалистами.
  7. Обязательный контроль безопасности модуля не тем, кто разработал данный модуль.
  8. Безопасность должна обеспечиваться в конфигурации по умолчанию и при развёртывании.
  9. Особый контроль за предоставлением прав на внесение изменений в ПО.
  10. «Площадь уязвимости» (потенциальная) должна быть как можно меньше (всевозможные открытые TCP/UDPпорты, запускаемые и зависимые службы, динамические веб-страницы, части приложения или службы, запускаемые с высокими привилегиями и т.д.).
  11. Должны быть защищены все уровни (независимо друг от друга и от других уровней защиты).
  12. Используйте правило минимальных привилегий (и + грамотно составленный ACL).
  13. Следует вести разработку с учётом аксиомы: внешние системы по умолчанию не защищены.
  14. Разработайте план действий на случай сбоев или отказов.
  15. Не стройте систему безопасности на ограничении информации о ПО.
  16. Разделяйте код и данные (исключение любой смеси данных и JS- или SQL-кода).
  17. Исправляя ошибки в защите, проверяйте всю систему – все модули, пытаясь найти там те же проблемы.

Общие принципы безопасного кодирования

1.Не предоставляйте взломщику никакой информации.

2.Не позволяйте информации просочиться через заголовки.

3.Не включайте ничего лишнего в код!

4.Следите за квотами, буферами и сериализацией.

5.Используйте стандартные средства операционной системы.

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

7.Не размещайте никаких пользовательских файлов в каталоге \Program Files.

8. Безопасно создавайте временные файлы (GetTempPath, GetTempFileName)

9. Никаких внутрикорпоративных имен в приложении!

10.Ведите журналы безопасности в приложении.


Безопасность написания защищённого кода на конкретных примерах

Поговорим об основных проблемах и принципах программирования, связанных с написанием безопасного приложения:

1. Переполнение буфера (стека, кучи, переполнение в результате ошибок индексации массивов, переполнение в результате использования некорректной кодировки). 

Способ лечения: строгая проверка всех входных данных на корректность во всех отношениях, аккуратность при обработке данных; проверка корректности подаваемых на вход

2. Использование злоумышленниками ПО или его службы, запущенного/ой с высокими привилегиями (примеров много, и они очевидны). 

Решение: 
а) Выясните, какие ресурсы нужны ПО; 
б) выясните, какие API использует ПО;
в) определите, какая требуется учётная запись и какой ей нужен маркер;
г) отладка ошибок, возникающих из-за ограничения привилегий.

3. Слабые случайные числа. 

Решение: использовать ГСЧ на основе хороших блочных шифров с полным набором раундов, работающих в режимах CBC, CFBили OFB (хотя допускается и режим ECB). Генерация – только на основании пароля, который в свою очередь должен удовлетворять определённым условиям сложности (в ПО должна быть проверка).

4. Слабая криптография. 

Решение: использование по возможности краткосрочных (сеансовых) ключей. Аутентификационные данные должны храниться строго централизованно, а обрабатываться – локально. В случае возникновения задачи обмена ключами по небезопасному каналу – использовать ассиметричную криптографию, передавая таким способом лишь ключи к симметричному шифру. В случае оборота ЭД, использовать механизм ЭЦП.

5. Универсальная защита конфиденциальных данных. 

Проблема: её нет. 
Решение: шифрование данных мощным симметричным алгоритмом (AES, RC6, Blowfish, 3DESи т.д.), хранение пароля в надёжном разделе реестра (в случае несистемного использование – вообще нехранение пароля!), требование ввода пароля, защита списками ACL файла и раздела реестра.

6. Опасность входных данных (SQL-injection, XSSи многое другое). Проблема: очевидна. Решение: особое внимание всякого рода регулярным выражениям; строгий контроль корректности и длины входных данных и их фильтрация. Указывайте полные пути в адресах, представленные в канонической форме.

7.Опасности работы с БД. 

Решения: 
а) Использование параметризованных запросов к БД, отсутствие конструирования запросов внутри приложения; жёсткий контроль корректности отправляемых запросов; б) не подключаться к БД от имени system; 
г) Используйте безопасно хранимые процедуры.

8. Защита от XSS-атак: 
а) кодирование выходных данных; 
б) использование двойных кавычек во всех атрибутах тэга; 
в) как можно чаще используйте innerText; 
г) используйте только одну кодовую страницу.

Комментариев нет:

Отправить комментарий

Вы можете добавить свой комментарий...