С современными фреймворками совершать ошибки (не) сложнее
Эта часть курса посвящена проблемам безопасности, которые разработчики создают при работе с современными фреймворками, а также способам, с помощью которых разработчики программного обеспечения могут быстро снижать такие риски.
Типовые проблемы безопасности (почти) решены
В четвертой части этого курса мы рассмотрели несколько наиболее распространенных проблем безопасности в программном обеспечении. Они перечислены в списке OWASP Top Ten, который содержит десять наиболее распространенных недостатков безопасности в программном обеспечении. Список выглядит так:
- Инъекции
- Нарушенная аутентификация
- Раскрытие конфиденциальных данных
- Внешние сущности XML (XXE)
- Нарушенный контроль доступа
- Ошибки конфигурации безопасности
- Межсайтовый скриптинг (XSS)
- Небезопасная десериализация
- Использование компонентов с известными уязвимостями
- Недостаточное журналирование и мониторинг
Современные фреймворки стремятся предоставлять инструменты для противодействия этим и многим другим проблемам по умолчанию. Например, многие уязвимости типа инъекций связаны с динамическими SQL-запросами, которые не используют параметризованные запросы или, например, существующие ORM-библиотеки. Аналогично, слабая аутентификация и управление сессиями часто связаны с тем, что разработчики создают собственную функциональность управления сессиями, в которой затем появляются ошибки. Многие проблемы XSS связаны с возможностью напрямую выводить на страницу HTML-содержимое, введенное пользователем; обычно об этом заботятся шаблоны представлений.
Во фреймворках есть функциональность для добавления контроля доступа на уровне функций. Они также обычно предоставляют средства для предотвращения атак межсайтовой подделки запроса, прозрачно создавая скрытые токены для каждого запроса, связанного с данными формы.
...но проблемы часто находятся в бизнес-логике
Хотя фреймворки обеспечивают значительную защиту от самых распространенных проблем безопасности, они не защищают от ошибок в бизнес-логике приложения. Консультант по безопасности ищет не только уязвимости, связанные с тем, что пользователь может взломать систему. Он также ищет уязвимости, из-за которых пользователь может выполнить действие, которое изначально не должен иметь возможности выполнять.
Цикл разработки программного обеспечения
Процесс разработки программного обеспечения включает сбор и анализ требований, планирование, реализацию, тестирование и сопровождение программного обеспечения. На этапе работы с требованиями разработчики и заинтересованные стороны определяют и документируют требования, связанные с разрабатываемым программным компонентом. Затем следует этап планирования, на котором разработчики решают, как эти требования будут реализованы в программном обеспечении. Этап реализации включает программирование и интеграцию существующих компонентов в программное обеспечение, а во время тестирования программное обеспечение проверяется как вручную, так и автоматически. После того как программное обеспечение или его части вводятся в промышленную эксплуатацию, сопровождение также становится частью процесса: разработчики поддерживают программное обеспечение в актуальном состоянии, исправляют обнаруженные проблемы и добавляют новые функции.
Естественная часть процесса разработки программного обеспечения — постоянный поиск решений, которые удовлетворяют отдельным требованиям. По сути, проблемы решаются через планирование и проверку разных вариантов до тех пор, пока не будет найдено подходящее решение. Если проблема и ее решение уже известны, исчерпывающий поиск не требуется, и разработчик фактически руководствуется эвристикой, усвоенной при выполнении похожих задач в прошлом.
Гибкие методологии разработки программного обеспечения, такие как ScrumBan, в основном направлены на то, чтобы сделать процесс разработки программного обеспечения видимым для заинтересованных сторон. Это упрощает улучшение рабочего процесса. Акцент делается на взаимодействии и людях, создании ценности, то есть работающего программного обеспечения, сотрудничестве с заказчиками для понимания их потребностей и реакции на изменения по мере того, как ожидания и потребности заказчиков со временем меняются. См. Манифест гибкой разработки программного обеспечения.
Определение требований
Требования определяются совместно с заказчиком и конечными пользователями. Они записываются для дальнейшего использования, например в форме функций или "пользовательских историй". Хорошие пользовательские истории — это то, во что можно INVEST, и они могут использоваться для создания SMART-задач.
Пользовательские истории обычно записываются с точки зрения пользователя, который ожидает от программного обеспечения конкретную функциональность. Типичный способ формулировать такие истории следует шаблону "Как ..., я хочу ..., чтобы ...", который включает заинтересованную сторону, функциональность и причину, по которой эта функциональность нужна. Например: "Как владелец ресторана, я хочу видеть ежедневные продажи разных позиций меню, чтобы определять, какие позиции меню прибыльны, а какие нет".
После того как пользовательские истории определены и записаны, или хотя бы часть из них записана, они приоритизируются совместно с заинтересованными сторонами, включая заказчика. После приблизительной приоритизации несколько пользовательских историй можно взять в разработку. Работая над пользовательскими историями, разработчики постепенно знакомятся с предметной областью и могут запрашивать у заказчика дополнительные уточнения по программному обеспечению. Когда пользовательские истории завершены, их показывают заказчику, а заказчик может запросить новые функции, заново приоритизировать существующие пользовательские истории и уточнить свои замечания о программном обеспечении.
Системы контроля версий
Исходный код программного обеспечения и документация обычно хранятся в системе контроля версий, которая используется для сопровождения изменений программного обеспечения, а также для его распространения при необходимости. На практике у каждого разработчика есть собственная "песочница", в которой можно разрабатывать программное обеспечение и пробовать разные варианты. Когда новое изменение отправляется в систему контроля версий, другие разработчики могут скачать это изменение, проверить его и посмотреть, как оно взаимодействует с их текущей версией системы.
Одна из наиболее распространенных систем контроля ве рсий — Git, которую также использует GitHub. Если вы с ними не знакомы, см. руководство Hello World от GitHub; более опытным пользователям может быть полезна книга Pro Git.
Непрерывная интеграция
Системы контроля версий часто подключаются к серверам непрерывной интеграции, которые запускают автоматизированные тесты программного обеспечения после каждого изменения в системе контроля версий. Когда тесты запускаются как в локальной песочнице разработчика, так и на сервере непрерывной интеграции, проявляются проблемы, которые не обнаруживаются при локальном тестировании. Они могут быть связаны, например, с используемой операционной системой, браузером, конфигурацией и так далее. Также возможно, что локальная песочница не включает все компоненты программного обеспечения, поэтому локально невозможно правильно протестировать изменения. Если тесты не проходят на сервере непрерывной интеграции, изменения либо откатывают, либо исправляют как можно скорее.
Примеры инструментов, используемых для непрерывной интеграции, включают Travis CI и Coveralls. Travis проверяет, что программное обеспечение компилируется и что тесты проходят после последнего изменения, а Coveralls предоставляет функциональность для оценки покрытия кода тестами. Покрытие кода, по сути, является показателем, который отражает количество строк кода, затрагиваемых тестами. Здесь также пол езен Cobertura.
Travis CI и Coveralls бесплатны для проектов с открытым исходным кодом.
Непрерывное развертывание
После того как новая функция завершена и автоматизированные тесты для нее проходят успешно, новая версия программного обеспечения развертывается на сервере. Это позволяет конечным пользователям попробовать новые функции и дать обратную связь. Новая версия может быть развернута либо на промежуточном сервере, который в основном используется для тестирования, либо сразу в производственной среде. Также возможны разные варианты такого развертывания: если есть много серверов, на которых размещено программное обеспечение, оно может быть развернуто, например, только на небольшой части серверов.
Один из вариантов размещения программного обеспечения — облачные сервисы, такие как Heroku. См., например, интеграцию Heroku с GitHub и интеграцию Travis с Heroku. Первая ссылка также содержит функциональность для интеграции с Travis CI.
DevOps
DevOps — это все более популярный набор практик разработки, цель которого — сократить разрыв между разработкой и системным администрированием. Эта цель достигается за счет усиления сотрудничества между администраторами и разработчиками. Это включает размещение администраторов и разработчиков в одних командах и создание рабочих процессов для быстрой итерации и выпуска функций. Кроме того, разработчикам предоставляют доступ к системам и возможность выполнять в них административные задачи, чтобы упростить решение проблем.
DevOps направлен на минимизацию ненужных задержек и максимизацию эффективности через небольшие, непрерывные и итеративные изменения. Обычно он опирается на автоматическое тестирование, непрерывную интеграцию и развертывание, которые упоминались выше. Новые изменения часто развертываются в производственной среде, что также приводит к необходимости автоматизации, оптимизации и упрощения процесса развертывания. Это также делает весь процесс более прозрачным и пригодным для аудита.
Автоматизация развертывания новых изменений может быть полезна при установке исправлений для защиты системы или систем от распространенных уязвимостей. Когда публикуются новые уязвимости, сохранение безопасности часто зависит от способности исправлять системы быстрее, чем их успеют эксплуатировать. Если развертывание является автоматической и частой операцией, установка исправлений тоже, скорее всего, будет очень быстрой.
Можно воспринимать каждое развертывание как риск, потому что оно может что-то сломать. Один из способов снизить риск новых развертываний — так называемые канареечные релизы. Если необходимо обновить несколько машин, изменения можно отправить только на небольшой процент узлов. Затем можно наблюдать за узлами, на которых работает более новый код, и проверять, работают ли они как ожидается. Если все в порядке, изменения можно развернуть в остальной части сети.
Важно, чтобы безопасность была частью рабочего процесса DevOps. Система непрерывной интеграции должна запускать автоматические проверки безопасности при каждом коммите, чтобы легко обнаруживаемые уязвимости выявлялись сразу. Такие проверки могут включать, например, поиск устаревших зависимостей, статический анализ кода и фаззинг-тестирование.
Рабочие процессы DevOps часто используют микросервисы, виртуализацию и контейнеризацию. Хотя системы, созданные с помощью этих практик, часто имеют лишь минимальный набор зависимостей, сами зависимости могут быть риском безопасности. Об этих компонентах часто забывают, и для них нет простого способа развернуть исправления безопасности. Это особенно серьезная проблема с контейнерами, когда обычно существует много контейнеров, а единственный способ установить исправления — собрать их заново.
Помимо обеспечения безопасности разрабатываемого программного обеспечения, важно, чтобы серверы, на которых оно работает, также были защищены. Распространенный способ решить эту проблему — использовать инструменты управления конфигурацией, такие как Ansible и Puppet. Эти инструменты автоматически обеспечивают установку и настройку правильных пакетов на сервере. Это повышает понимание того, что на самом деле работает в системах, чтобы все компоненты можно было аудировать и проверять. Эти ин струменты также могут следить за тем, чтобы программное обеспечение на серверах оставалось правильно сконфигурированным. Если в конфигурации что-то меняется, заинтересованные стороны могут быть уведомлены, а иногда инструмент может даже сам исправить проблему.
В этой части серии курса мы рассмотрели бизнес-логику программного обеспечения и цикл разработки программного обеспечения. В следующей части, которая является заключительной частью курса Securing Software, мы изучим безопасные архитектуры с использованием анализа потоков данных.
Не забудьте проверить свои баллы в инд икаторе в правом нижнем углу материала!