Простая и ужасающая история про шифрование — об Open Source, доверии и ответственности
8К открытий8К показов
Рассказывает Kacper Walanus,Senior-разработчик Ruby on Rails и тимлид в EL Passion
Задача
Я хотел написать простое приложение для шифрования и дешифрования сообщений. Алгоритм AES показался хорошим выбором, так что я начал с поиска подходящей библиотеки.
Решение
Я пишу на Ruby, так что сделал то, что сделал бы любой на моём месте — загуглил «ruby gem aes». И сразу же нашёл библиотеку под названием (сюрприз!) «aes», простейший пример использования которой выглядел вполне понятно:
Если передать неверный ключ, то библиотека выдаст ошибку:
Баг
Во время разработки я заметил одну интересную особенность. Я написал тест для проверки расшифровки сообщений с неправильным ключом. Если говорить конкретнее, я заменил один символ в ключе, с которым сообщение было зашифровано и попытался получить обратный результат, ожидая ошибку. И… мой тест провалился! Ошибка не просто не была сгенерирована, ещё и само сообщение было корректно расшифровано:
Хорошо, допустим я нашёл один крайне особенный случай, один на миллион. Попробуем поменять два символа в ключе:
… и опять тот же результат!
Что же, осталась только одна вещь, которую стоило проверить — использовать совершенно другой ключ:
Это выглядело как серьёзная проблема безопасности, так что я решил разобраться, в чём дело.
Отладка
Проблема была вот в этой строчке библиотеки «aes»:
Для начала объясню часть про unpack
. В данном случае эта функция разделяет строку на массив из 32 строк (смотрите документацию, если нужны подробности):
Затем для каждой из этих 32 коротких строк вызывается метод #hex
. А String#hex
в Ruby, как известно, преобразует шестнадцатеричные строки в их десятеричное числовое представление, а в случае неудачи возвращает 0:
Таким образом, любая строка не содержащая корректной шестнадцатеричной последовательности (как «ff» или «13») превратится просто в 32 нуля:
И в результате расшифровать сообщение можно практически с любым ключом. Скорее всего, автор ожидал, что в качестве ключей будут использоваться корректные числовые последовательности. Описанный баг, вероятно, следствие данного наивного предположения.
Итого
Библиотека «aes» не самая большая среди прочих в Ruby, всего 45 звёзд и 13 форков. Но проблема в том, что она выдаётся на первом месте в Google при поиске «aes gem» или «ruby aes gem», а мы обычно не задаём вопросов к тому, что Google выдаёт в топе. Мы вообще очень редко проверяем внешние библиотеки, хотя, судя по всему, должны бы. Особенно, когда дело касается безопасности.
Дополнение
Хочу пояснить, что у меня не было цели обвинить в чём-либо автора библиотеки. Он написал её несколько лет назад и не мог предвидеть, что она станет выдаваться поисковиками на первых строчках в 2017 году. Я просто нашёл серьёзный баг и хочу поделиться им с другими. Это то, как я представляю общественную ответственность в движении Open Source.
Технические детали
- Сама библиотека: https://github.com/chicks/aes
- Версия, использованная автором: 0.5.0 / 12c3648
- Пример кода
8К открытий8К показов