Defensive Programming. Звучит здорово, но что это такое?

в 10:45, , рубрики: методика, Песочница, Программирование, метки: , ,

Defensive Programming

Безопасное программирование это форма безопасного планирования, предназначенная для продолжительного функционирования частей ПО, несмотря на непредвиденное использование указанного ПО. это был вступительный заголовок на англоязычной википедии, как мне удалось его перевести. По логике скорее получается что безопасное программирование просто вытекает из принципов безопасного планирования и заключается в том, что любая часть нашего ПО должна выдерживать нестандартное использование данного ПО. В связи с этим я решил описать тут эту статью так, как смог понять её сам(если я оказался в чем-то не прав — поправьте меня).
Эту идею можно рассматривать как снижение или устранение перспективы закона Мёрфи(закона подлости, если попроще).
Методы безопасного программирования используются прежде всего тогда, когда часть ПО может быть использована неправильно, специально или по неосторожности, что может привести к катастрофическим эффектам.

тыц

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


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

Иначе что?

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

Защищенное программирование:

В среде ИТ-специалистов безопасное программирование иногда называют защищенным программированием. По их заявлениям — такой подход минимизирует баги.
Ошибки в ПО — потенциальная лазейка, используемая взломщиками для внедрения кода(code injection), атаки системы(запрещающие обслуживание или другие атаки).

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

	int risky_programming(char *input){
		char str[1000+1];     // + один добавочный символ для NULL-символа
		// ...
		strcpy(str, input);   // Копируем input
		// ...
	}

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

		int secure_programming(char *input){
			char str[1000];
			// ...
			strncpy(str, input, sizeof(str)); // копирует только sizeof(str) символов из input. В данном случае копируется только 1000 символов, остальное отбрасывается
			str[sizeof(str) - 1] = ''; // завершаем строку нулевым символом.
			// ...
		}
Методы.

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

Звучит неплохо,

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

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

Спойлер

вот он) наш старый компьютер...

У многих программных продуктов, возникли проблемы с наследованием старого исходного кода. Например:

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

    Все тот же бабушкин компьютер. Ничего особого уже не может, но зато на нем можно поиграть в третьих героев

Парочка примеров

  1. Изначально код, был разработан для ввода ASCII данных. Теперь же для ввода данных используется UTF-8.
  2. Изначальный код был скомпилирован и протестирован на 32-битной архитектуре, но при компиляции на 64-битной архитектуре, могут возникнуть новые арифметические проблемы(такие как недействительные приведения типов и прочие)
  3. Если наследуемый код изначально предназначался для offline-машин(Компьютеров не подключенных к ЛС), то может случиться что код становится уязвимым при подключении к сети.

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

Известные примеры проблем наследования

  • BIND 9, был представлен Полом Викси и Дэвидом Конрадом как «BINDv9 is complete rewrite(Полностью переписан)», «Security was a key consideration in design(Безопасность являлась ключевым фактором при проектировании)», называя безопасность, надежность и новые протоколы ключевой задачей для переписывания старых исходников.
  • Microsoft Windows пострадали от уязвимости в метафайлах Windows и другими эксплойтами связанными с форматом WMF. Центр обеспечения безопасности Microsoft описывает WMF-функции как «примерно в 1990 годах, была добавленна поддержка WMF… Это было другое время с точки зрения направленности на безопастность...(я бы сказал „с точки зрения обеспечения безопасности“, но смысл состоит немножко в другом)… все было полностью проверенно», не были разработаны с учетом инициатив безопасности Microsoft.
  • Oracle борется с проблемами наследия, такими как старые исходники написанные без учета проблем обращения SQL-инъекций и расширения привилегий. В результате многие уязвимости, на которые было выделено время для исправлений, были не полностью исправлены. Это вызвало резкую критику со стороны экспертов в области безопасности. Также дополнительную критику вызывало то, что установки по умолчанию(в основном наследие старых версий), не совпадали с их собственными рекомендациями безопасности, некоторые из которых трудно изменить, так как многие приложения требуют менее безопасной настройки наследования для правильного функционирования.

Безопасная обработка ввода и вывода(input и output(фиг знает как оно правильно должно звучать по русски)):

  • Безопасная обработка ввода и вывода относится к безопасной методике программирования, предназначенной для предотвращения багов и их использования.
  • Обработка вводных данных:
    • Обработчик вводных данных — приложение, сервер, или другая компьютерная система обрабатывающая получаемые от пользователя, клиента или компьютерной сети вводные данные.
    • Безопасная обработка вводных данных необходимо для предотвращения уязвимостей, связанных с инъекцией кода, обхода каталогов, и прочих.
  • Также к безопасной обработке вводных данных относятся проверка вводных данных, завершение операции при некорректных данных, фильтрация, шифрование и прочее.
Канонизация

  • Взломщики с легкостью изобретают новые виды предоставления неверных данных. Например, если при проверке запрошенный файл не является "/etc/passwd", взломщик может использовать другой вариант данного имени файла, например "/etc/./passwd".
  • Во избежание ошибок из-за неканонического ввода, используют канонизацию API.
Канонизация

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

  • Одна из самых распространенных проблем, это непроверенное использование структур неизменяемого размера и функций для данных динамического размера
    (проблема переполнения буфера)

    . Это характерно для строкового типа данных в C. Такие библиотечные Функции C, как gets, использовать нежелательно, так как максимальный размер буфера ввода не передается в качестве аргумента. А вот например библиотечная функция C, типа scanf, может спокойно использоваться, но это требует от программиста осторожности с выбором безопасного формата строк, используя их обработку перед использованием.
  • Шифрование/аутентификация всех важных данных, передаваемых по сети. Не пытайтесь реализовать собственную схему шифрования, а используйте вместо этого проверенные схемы.
    Фигасе

    А как же тогда они должны развиваться, если не реализовывать новые схемы?
  • Все данные важны, пока не доказано обратное.
  • Все данные испорчены, пока не доказано обратное.
  • Весь код небезопасен, пока не доказано обратное:
    • Вы не можете проверить безопасность любого кода на уровне пользователя. Более канонически утверждение звучит так: «Никогда не доверяйте клиенту»
  • Если данные надо проверить на правильность, убедитесь в том что данные верны. Не следует проверять данные на то, что они неверны.
Проектирование по контракту:

  • Проектирование по контракту использует предварительные условия, постусловия и инварианты, чтобы предоставленные данные(и состояние программы в целом) были обработаны. Это позволяет сделать код лучше, документировать его допущения, и делать его безопасным. Также, оно может включать в себя проверку аргументов функции или методов до выполнения тела функции. Возврат значения перед выходом(break/return/throw/error code) также является плюсом.
  • Утверждения:
    • Внутри функции, вы можете проверить, что вы не ссылаетесь на что-либо не валидное(например NULL) и что длинна массива валидна перед обращениям к элементам, особенно на всех временных/локальных экземплярах. Хорошей эвристикой считается недоверие к библиотекам, написанным не вами. Таким образом, проверяйте все, что вы получили от них, после каждого их вызова. это часто помогает в создании маленькой библиотеки «утверждающих» и «проверяющих» функций, запускаемых совместно с логгером, таким образом вы можете трассировать свои пути и сократить обширные циклы отладки. С появлением аспектно-ориентированного программирования многие из утомительных аспектов безопасного программирования смягчаются.
  • Предпочтение исключений для кода возврата(return):
    • Вообще говоря, желательно выбрасывать понятные исключения вместо возврата значений, к которым, другие программисты, скорее всего не готовы. Это позволяет минимизировать жалобы клиента (или разработчика) и повышает надежность и безопасность вашего ПО.
Итак. Краткие выводы.

  1. Сколько не используй код повторно, рано или поздно его придется переписать с нуля(читай заменить на нечто более новое).
  2. Обрабатывайте все вводные данные, без этого никуда, и поможет не выпасть в overflow.
  3. Наследование старого кода — это конечно хорошо, но лучше написать свежий код, с учетом всех требований.
  4. Методы защищенного программирования лучше использовать всегда. Да и канонизацию тоже никто не отменял.

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

Автор: ISAroot

Источник

Поделиться

* - обязательные к заполнению поля