Дэвид Лебланк - 19 смертных грехов, угрожающих безопасности программ
Родственные грехи
Из–за предсказуемости случайных чисел криптосистема может оказаться ненадежной. Так, один из способов неправильного применения SSL как раз и заключается в использовании недостаточно хорошего источника случайности, в результате чего противник может угадать сеансовый ключ. Ниже в этой главе мы приведем соответствующий пример.
Где искать ошибку
Этот грех может проявиться в любой ситуации, когда нужно хранить некоторые данные в секрете, не допуская даже случайного угадывания. Вне зависимости от того, используется шифрование или нет, наличие хорошего источника случайных чисел – одно из ключевых требований к безопасности системы.
Выявление ошибки на этапе анализа кода
Шагов не так много:
□ выявить места, в которых следовало бы пользоваться случайными числами, но это не делается;
□ выявить места, где применяются PRNG–генераторы;
□ убедиться, что для всех применяемых CRNG–генераторов используется затравка надлежащего качества.
Когда следует использовать случайные числа
Задача выявления всех мест, где надо было бы применить случайные числа, но это не сделано, может оказаться очень трудной. Для ее решения нужно понимать, какими данными манипулирует программа, а часто еще и детально разбираться в используемых библиотеках. Например, старые криптографические библиотеки ожидают, что затравку CRNG–генератору вы зададите самостоятельно. В первых версиях библиотека продолжала работать, даже если вы никакую затравку не задали. Позже в этом случае стали возвращать сообщение об ошибке (или вообще завершать программу). Но вошло в привычку задавать в качестве затравки фиксированное значение, чтобы заставить библиотеку «заткнуться». В наше дни практически все криптографические библиотеки получают затравку непосредственно от системы.
Мы рекомендуем хотя бы посмотреть, как реализовано генерирование идентификаторов сеансов. В большинстве серверов приложений третьих фирм эта проблема осознана и решена, но когда программист реализует собственную схему управления сеансовыми идентификаторами, то часто делает это неправильно.
Выявление мест, где применяются PRNG–генераторы
Здесь мы хотим показать, как найти криптографические и некриптографические генераторы псевдослучайных чисел, которые, возможны, неправильно затравлены. Если используется системный CRNG–генератор, то поводов для беспокойства обычно нет, так как в них, скорее всего, применяются хорошие затравки.
Как правило, те, кто использует некриптографический PRNG–генератор, обращаются к API, поставляемому вместе с языком программирования, поскольку не знают ничего лучшего. В табл. 18.1 перечислены некоторые из стандартных библиотечных функций.
Для CRNG–генераторов редко существует стандартный API, разве что его предоставляет используемая криптографическая библиотека. Если это так, можете пользоваться им, обычно это безопасно.
Есть несколько стандартных подходов. Сейчас криптографы отдают предпочтение блочным шифрам (обычно AES) в режиме счетчика. Еще один популярный генератор описан в стандарте ANSI Х9.17. Столкнувшись с любым из них, ищите все случаи употребления симметричной криптографии и самостоятельно попытайтесь определить, корректно ли генератор реализован и хорошо ли затравлен.
Таблица 18.1. Некриптографические генераторы псевдослучайных чисел в популярных языках
Правильно ли затравлен CRNG–генератор
Если затравку для CRNG–генератора формирует система, то риска, скорее всего, нет. Но в языке типа Java, где API не пользуется системным генератором или не задействует CRNG напрямую, у вас может быть возможность задать затравку самостоятельно. И этим часто пользуются хотя бы для того, чтобы ускорить инициализацию. (В Java это случается часто, поскольку инициализация класса SecureRan–dom происходит довольно медленно; см. раздел «Java» ниже в этой главе.)
Есть и другая крайность – когда затравка фиксирована. В таком случае систему следует считать небезопасной. Если затравка хранится в файле и периодически обновляется значением, которое выработано генератором, то безопасность зависит от того, насколько хороша была первоначальная затравка и насколько надежно защищен файл.
Если используется код для получения энтропии сторонней фирмы, то степень риска определить сложно. (Теория энтропии находится за рамками данной книги.) Хотя в таких случаях риск может быть невелик, все же, если есть возможность воспользоваться системным генератором, мы рекомендуем так и поступить.
Есть лишь два случая, когда такой возможности нет, – это необходимость повторно воспроизвести поток случайных чисел (встречается очень редко) и работа с операционной системой, не поддерживающей такого механизма (в наши дни это разве что некоторые встраиваемые системы).Тестирование
В некоторых случаях к генерируемым случайным числам можно применить статистические тесты, но делать это с помощью автоматизированных средств на этапе контроля качества не очень практично, поскольку оценивать результаты работы генератора случайных чисел часто приходится опосредованно.
Самый известный набор тестов предложен в стандарте генератора случайных чисел FIPS 140–1 (Federal Information Processing Standard – федеральный стандарт по обработке информации). Один из тестов работает непрерывно, а остальные предполагается запускать в момент инициализации генератора. Обычно гораздо проще включить их прямо в код генератора, чем применять как–то иначе.
Примечание. Тесты типа FIPS абсолютно бесполезны для данных, вырабатываемых CRNG–генератором. Они имеют смысл лишь для проверки качества истинно случайных чисел. Данные же, получаемые на выходе CRNG, обязаны проходить все статистические тесты с очень высокой вероятностью, даже если числа стопроцентно предсказуемы.
В отдельных случаях, когда вы хотите убедиться, что в некоторой части программы действительно используются случайные данные, имеет смысл посмотреть на несколько последовательных значений. Если они более–менее равномерно распределены по большому пространству (64 и более битов), то беспокоиться, наверное, не о чем. В противном случае нужно изучить реализацию. В любом случае увеличение значения на 1 – это очевидная проблема.
Примеры из реальной жизни
Есть несколько примеров игровых сайтов, которые пали жертвой низкого качества случайных чисел (см. раздел «Другие ресурсы»). Немало также примеров на тему недостаточно случайных идентификаторов сеанса. Но мы обратим внимание на некоторые курьезные ошибки, а именно на плохую реализацию генератора случайных чисел в самом криптографическом коде.
Браузер Netscape
В 1996 году студенты–старшекурсники Ян Голдберг и Дэвид Вагнер обнаружили, что в реализации SSL в браузере Netscape «случайные» сеансовые ключи создаются путем применения алгоритма MD5 к не совсем случайным данным, в том числе системному времени и идентификатору процесса. В результате на тогдашнем оборудовании они смогли взломать реальный сеанс за 25 с. Сейчас это заняло бы меньше доли секунды. Вот так–то!
Компания Netscape придумала протокол SSL для использования в своем браузере (первая публичная версия называлась Netscape–designed Version 2). Ошибка содержалась в реализации, а не в самом проекте протокола, но ясно показала, что Netscape – это не та компания, которая может спроектировать безопасный транспортный протокол. Время подтвердило это мнение. Разработка версии 3 протокола была поручена профессиональному криптографу, который справился с задачей куда лучше.
Проблемы в OpenSSL
Совсем старые версии OpenSSL требовали, чтобы затравку для PRNG задавал пользователь. Если это не было сделано, то библиотека ограничивалась предупреждением «Random number generator not seeded» (генератор случайных чисел не затравлен). Некоторые программисты его игнорировали, и программа продолжала работать дальше. Другие задавали в качестве затравки фиксированную строку, программа и в этом случае была довольна.
Когда вошло в моду псевдоустройство /dev/random, для затравки PRNG стали использовать получаемое от него (вместо /dev/urandom) значение. В то время в ОС FreeBSD–Alpha еще не было устройства /dev/random, поэтому на данной платформе библиотека OpenSSL молчаливо продолжала работать по–старому (см. CVE CAN–2000–0535).
Потом обнаружились ошибки в PRNG, разработанном компанией Netscape (при определенных условиях противник мог вычислить состояние генератора и предсказать случайные числа). Это произошло, несмотря на то что в основе алгоритма лежала популярная криптографическая функция (см. CVE–2001–1141).
Раз уж такие ошибки возможны даже в распространенных криптографических API, вообразите, что может произойти, если вы решите самостоятельно разработать систему для генерации случайных чисел. На создание гарантированно безопасных генераторов затрачено очень много усилий. Если вам никак не обойтись без собственного, то хотя бы воспользуйтесь их плодами. В следующем разделе мы покажем, как это можно сделать.