Фаззинг — это метод тестирования, который автоматически подаёт программе неожиданные входные данные для поиска ошибок. На этой странице объясняется, как он работает и как начать тестировать собственный код (она не описывает тестирование чужих систем).
Что такое фаззинг
OWASP описывает фаззинг как метод тестирования ПО, направленный на выявление ошибок, уязвимостей или неожиданного поведения. Он автоматически подаёт некорректные, неожиданные или частично некорректные входные данные и отслеживает, ведёт ли себя программа неправильно. Глоссарий NIST (со ссылкой на NIST SP 800-95) так же описывает fuzz testing как инструменты, называемые фаззерами, которые передают тестируемой цели комбинации входных данных, чтобы показать, как она реагирует.
Его сильная сторона в том, что он почти не опирается на предположения о том, как должна работать система. Тесты, написанные вручную, охватывают только случаи, которые кто-то продумал. Фаззер может найти места, где система ломается на входных данных, которые никто не предполагал.
Как это работает
Большинство современных фаззеров управляются покрытием. Они измеряют, какой код выполнился, сохраняют входные данные, которые попали в новый код, и мутируют их для создания следующих. Так удаётся глубже проникнуть в программу, чем при одних только случайных данных. libFuzzer от LLVM описан в документации как внутрипроцессный, управляемый покрытием эволюционный движок фаззинга, который встраивается в тестируемую библиотеку.
Распространённые инструменты
| Инструмент | Основные цели | Примечания |
|---|---|---|
| libFuzzer | C/C++ и другие (LLVM) | Встраивается в тестируемый код. Согласно документации, исходные авторы перешли на движок Centipede, и теперь libFuzzer получает только важные исправления ошибок |
| AFL++ | C/C++ и другие | Широко используемый фаззер с управлением по покрытию |
| Встроенный фаззинг Go | Go (1.18 и новее) | Запускается командой go test -fuzz. Входные данные, вызвавшие сбой, сохраняются в testdata и воспроизводятся обычными тестами |
| OSS-Fuzz | ПО с открытым исходным кодом | Бесплатный непрерывный сервис фаззинга от Google, использующий несколько движков и санитайзеров |
Fuzz target для libFuzzer — это функция (LLVMFuzzerTestOneInput), которая получает байты входных данных и их длину. В Go вы пишете тестовую функцию с именем FuzzXxx, которая принимает *testing.F, и добавляете исходные входные данные через f.Add.
Как начать на собственном ПО
- Выберите функцию, которая разбирает входные данные (парсер формата файла, загрузчик конфигурации, обработчик протокола)
- Напишите небольшую fuzz target, которая просто передаёт в неё входные данные. Сделайте её быстрой и детерминированной, без состояния между вызовами
- Добавьте несколько типичных корректных входных данных как seeds
- Включите санитайзер, например AddressSanitizer, и запускайте на заданное время (в Go длительность задаёт
-fuzztime) - Превратите каждый входной набор, вызвавший сбой, в воспроизводимый тест и сохраните его как регрессионный тест после исправления
- Чтобы продолжать, запускайте короткие сессии в CI или, для открытого ПО, рассмотрите участие в OSS-Fuzz
В фаззинге Go есть ограничение в 1 секунду на одно выполнение, а управление по покрытию работает на AMD64 и ARM64. Ограничения в вашей среде проверьте в официальной документации.
Фаззите только то ПО, которым вы управляете
Фаззинг постоянно отправляет большие объёмы некорректных данных. Делать это с сервисом или сервером, на тестирование которых у вас нет разрешения, может быть расценено как несанкционированный доступ или нарушение работы сервиса. Ограничьтесь ПО, которое вы разрабатываете или сопровождаете, в собственной среде.
Что он может и чего не может
OWASP указывает, что фаззинг дополняет, а не заменяет ручной анализ кода и отладку. Также отмечается, что фаззеры чаще находят простые ошибки, а при black-box тестировании оценить влияние найденного сложнее.
Поэтому фаззинг силён в задаче «позволить машине искать способы, которыми код может сломаться», и слаб в проблемах на уровне проектирования, например в отсутствии авторизации. Для таких проблем, как IDOR, полагайтесь на тесты, которые проверяют заданные правила.
Позиция этого сайта: найти входные данные — это половина работы; сохраните их как регрессионный тест
Важно не только запустить фаззер, но и сохранить найденное как регрессионный тест. Встроенный фаззинг Go, который сохраняет входные данные, вызвавшие сбой, в testdata и воспроизводит их в обычных тестах, следует этой идее. Это не даёт исправленной ошибке вернуться после последующих изменений. Если вы отвечаете за код, который разбирает входные данные, разумный первый шаг — фаззить одну функцию.
Источники (первоисточники)
- OWASP Community, "Fuzzing" — owasp.org (определение, характеристики, ограничения)
- NIST CSRC Glossary, "fuzz testing" — csrc.nist.gov (источник: NIST SP 800-95)
- LLVM, "libFuzzer" — llvm.org (принцип работы, fuzz target, статус поддержки)
- Go, "Fuzzing" — go.dev (Go 1.18 и новее, порядок запуска, ограничения)
- Google, "OSS-Fuzz" — google.github.io (обзор, поддерживаемые движки). Количество найденных ошибок меняется со временем, поэтому здесь они не приводятся. Для AFL++ актуальное использование смотрите в его официальной документации
Читайте далее
- Глоссарий: Что такое DAST (динамическое тестирование безопасности приложений) · Что такое path traversal · Что такое CVE
- Глоссарий: Что такое OWASP Top 10
- Руководство: Аудит зависимостей с помощью OSV-Scanner