Подтвердить что ты не робот

Как вы можете обеспечить безопасное кодирование с помощью Test Driven Development?

Я приступил к работе над последней тенденцией, которая является Test Driven Development (TDD). Большая часть разработки я делаю на C или С++. Мне кажется, что существует очень очевидный конфликт между обычными методами TDD и распространенными методами безопасного кодирования. При этом TDD сообщает вам, что вы не должны писать новый код для чего-то, для которого у вас нет теста на неудачу. Для меня это означает, что я не должен писать защищенный код, если у меня нет модульных тестов, чтобы проверить, защищен ли мой код.

Это вызывает две проблемы:

  • Как я могу эффективно выполнять модульные тесты для проверки переполнения буфера,, переполнения кучи, ошибок индекса массива, ошибок в строках формата, ANSI vs Unicode vs MBCS с ошибками размера строк, безопасной обработки строк (из Howard и LeBlanc "Написание защищенного кода" )?

  • В какой момент в стандартной TDD-практике эти тесты должны быть включены, поскольку большая часть безопасности не работает.

Удивительно, но я нашел очень мало исследований, посвященных TDD и безопасности. Большинство из того, что я встретил, - это документы TDD, которые упоминают на очень высоком уровне, что TDD "сделает ваш код более безопасным".

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

Спасибо!

EDIT:

Тема Fuzzing появилась, и я думаю, что это отличный подход к этой проблеме (в целом). Это вызывает вопросы: ли Fuzzing вписывается в TDD? Где в TDD-процессе работает fuzzing?

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

ИЗМЕНИТЬ 2:

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

Хорошо известная проблема в обеспечении безопасности программного обеспечения: если вы защищаете от 5 сценариев атаки, злоумышленник будет искать и использовать 6-ю атаку. Это очень сложная игра в кошки-мышки. Предоставляет ли TDD какое-либо преимущество против этого?

4b9b3361

Ответ 1

Да, TDD - это инструмент/метод, который может помочь обеспечить безопасное кодирование.

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

Неизвестные угрозы

Как вы указали в редакторе 2: "вы защищаете от 5 сценариев атаки, злоумышленник будет просто искать и использовать 6-ю атаку". TDD не собирается защитить вас от неизвестных угроз. По самой своей природе вы должны знать, что вы хотите проверить, чтобы написать тест в первую очередь.

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

TDD поможет следующее:

  • Тесты могут быть записаны для проверки угрозы.
  • Решение может быть реализовано для блокировки угрозы и быстрого подтверждения работы.
  • Что еще более важно, если все остальные тесты все равно пройдут, вы можете быстро проверить, что:
    • Все другие меры безопасности по-прежнему ведут себя правильно.
    • Все остальные функции по-прежнему ведут себя правильно.
  • В основном TDD помогает обеспечить быстрое время отклика с момента обнаружения угрозы, когда решение станет доступным.
  • TDD также обеспечивает высокую степень уверенности в правильности поведения новой версии.

Тестируемый код

Я читал, что TDD часто неверно истолковывается как методология тестирования, когда на самом деле это скорее методология проектирования. TDD улучшает дизайн вашего кода, делая его более проверяемым.

Специализированное тестирование

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

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

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

Улучшенная конструкция

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

  • Тестировать компоненты, которые получают сетевые данные, не отправляя их по сети.
  • Можно легко изнашивать объекты, чтобы вести себя непредсказуемыми/ "нереалистичными" способами, которые могут возникать в сценариях атаки.
  • Тестовые компоненты изолированы.
  • Или с любым желаемым сочетанием производственных компонентов.

Тестирование устройств

Следует отметить, что TDD отличается высокой локализацией (модульное тестирование). В результате вы можете легко проверить, что:

  • SecureZeroMemory() корректно удалит пароль из ОЗУ.
  • Или что GetSafeSQLParam() будет правильно защищать от SQL-инъекции.

Тем не менее, становится все труднее проверить, что все разработчики использовали правильный метод в любом месте, которое ему требуется.
Тест для проверки новой функции, связанной с SQL, подтвердит, что эта функция работает - она ​​будет работать так же хорошо, как с "безопасными" и "небезопасными" версиями GetSQLParam.

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

  • Стандарты кодирования
  • Обзоры кодов
  • Тестирование

Ответ 2

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

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

Ошибки интерпретации строк (Unicode vs. ANSI) особенно хороши для тестирования с помощью TDD. Обычно просто перечислять плохие и хорошие вклады и утверждать об их интерпретации. Вы можете обнаружить, что вам нужно немного перестроить свой код, чтобы "сделать его проверяемым"; я имею в виду методы извлечения, которые изолируют строковый код.

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

Я не уверен, что есть простой ответ. Тестирование требует креативности, дисциплины и приверженности, но обычно стоит того.

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

Надеюсь, что это поможет

Ответ 3

TDD - лучший способ создать безопасную систему. Все программное обеспечение, разработанное Microsoft, является безумным, и это, возможно, причина номер один для резкого сокращения обнаруженных уязвимостей. Для этой цели я настоятельно рекомендовал использовать Peach Framework. Я лично использовал Персика с большим успехом в поиске переполнения буфера.

Файлы Peach pit предоставляют способ описания данных, используемых вашим приложением. Вы можете выбрать, какой интерфейс вы хотите проверить. Ваше приложение читает файлы? Есть ли у него открытый порт? После того, как вы сообщите персику, как выглядит вход и как общаться с вашим приложением, вы можете отключить его, и я знаю все неприятные данные, чтобы ваше приложение пошатнулось.

Чтобы все выполнялось, у персика отличная testing harness. Если ваше приложение выйдет из строя, персик будет знать, потому что у него есть отладчик. Когда ваше приложение выйдет из строя, персик перезапустит его и продолжит тестирование. Персик может классифицировать все сбои и сопоставлять основные дампы с вводом, который он использовал для сбоя приложения.

Ответ 4

Параметрированные тесты

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

Моя практика TDD

Мы работаем над приемочным тестированием на моей работе. Большинство наших тестов, как правило, близки к полному функциональному тестированию стека. Причина в том, что мы обнаружили, что было более ценным тестирование и обеспечение поведения действий, управляемых пользователем. Мы используем такие методы, как генерация динамических тестов из параметризованных тестов, чтобы обеспечить нам более полное покрытие с минимальной работой. Мы делаем это для ASCII против UTF8, API-соглашений и хорошо известных вариантов тестов.

Ответ 5

Тема Fuzzing появилась, и я думаю, что это отличный подход к эта проблема (вообще). Это поднимает вопросы: подходит ли Fuzzing в TDD? Где в TDD-процессе работает fuzzing?

Я считаю, что это может поместиться неплохо! Есть fuzzers, такие как american fuzzy lop, которые могут быть написаны сценарием и сами адаптироваться к изменениям в формате ввода-вывода. В этом конкретном случае вы могли бы интегрировать его с Travis CI, хранить входные тестовые примеры, которые вы использовали, и запускать регрессионное тестирование против них.

Я могу расширить этот ответ, если вы придумаете какие-либо вопросы для деталей в комментариях.