Простая и ужасающая история про шифрование — об Open Source, доверии и ответственности

Рассказывает 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.

Технические детали

Источник: пост на blog.elpassion.com