[Перевод] Поиск секретных данных в исходном коде

Когда разработчики вносят прямо в исходный код секретные данные, вроде паролей и ключей API, эти данные вполне могут добраться до общедоступных репозиториев.

Я — разработчик, и я признаю то, что раньше допускала попадание секретных данных в открытые GitHub-репозитории. Подобные данные, жёстко заданные в коде, всегда были проблемой в различных организациях. Я, выполняя испытания на проникновение с целью проверки систем защиты компаний, всегда в первую очередь исследую код этих компаний на наличие в нём секретных данных. Если разработчик вносит что-то вроде паролей в код, эти данные могут оказаться в общедоступных репозиториях или в пакетах приложений, а после этого могут попасть в руки злоумышленников.

tgwxmdpsmtd8pugga-jm6r__ope.jpeg

По мере того, как микросервисные архитектуры и приложения, построенные вокруг неких API, получают всё более и более широкое распространение, разработчики часто нуждаются в программных механизмах обмена идентификационными данными и другими секретными сведениями. А это значит, что программисты, работая с подобными данными, иногда могут совершать ошибки.
Рассмотрим практический пример утечки идентификационных данных, вписанных прямо в исходный код одной системы. Вот отчёт об ошибке, касающийся reverb.com. Исследователь обнаружил в коде идентификационные данные, используемые для доступа к Cloudinary. Секретный ключ присутствовал в исходном коде Android-приложения Reverb. Любой, кто загрузит это приложение, может извлечь соответствующие учётные данные и получить возможность, чтения, редактирования и удаления файлов, хранящихся в соответствующем экземпляре Cloudinary:

private static final java.lang.String CONFIG = "cloudinary://434762629765715:█████@reverb";

И подобные уязвимости — это вовсе не редкость. Я занимаюсь тестированием систем на устойчивость к проникновениям, и могу заявить, что мне доводилось находить в общедоступном коде или в скомпилированных файлах многих организаций самые разные секретные данные. Среди них — учётные данные для аутентификации в различных службах, ключи AWS, ключи к API GitHub. Иногда хакеру, который хочет взломать некую компанию, достаточно всего лишь поискать в её GitHub-репозитории случайно отправленные туда учётные данные для входа в различные системы.

Использование регулярных выражений


Как обнаруживать в коде секретные данные до того, как их попадание в общий доступ приведёт к утечке информации из организации? Самый простой и понятный способ поиска таких данных, жёстко внесённых в код, заключается в использовании инструментов текстового поиска и регулярных выражений.

Если в коде имеются ключи API, ключи шифрования, пароли к базам данных, их часто можно обнаружить, воспользовавшись инструментами для поиска ключевых слов, вроде grep. Искать, например, можно по словам key, secret, password или aws. При таком подходе осуществляется поиск по идентификаторам, вроде имён переменных, которые используются для хранения интересующих нас данных. Аналогично — можно пользоваться поиском по текстам для нахождения связанных с секретными данными имён файлов и служебных данных, характерных для файлов определённого формата. Например, можно поискать по строке -----BEGIN RSA PRIVATE KEY-----.

Многие ключи API, кроме того, записываются с использованием определённого формата данных. Обнаружить такие ключи можно, выполнив поиск с применением регулярных выражений. Например, идентификаторы ключей доступа к AWS обычно начинаются со строки AKIA, за которой следует 16 алфавитно-цифровых символов. Поэтому, если выполнить поиск с использованием регулярного выражения AKIA[0–9A-Z], можно обнаружить в коде соответствующие ключи.

Ключи для API Twilio начинаются с символов SK, за которыми следуют 32 алфавитно-цифровых символа. А это значит, что найти их можно с помощью регулярного выражения SK[a-z0–9]{32}. Пароли в URL можно обнаружить, выполнив поиск по шаблонам, соответствующим базовым синтаксическим конструкциям, используемым в соответствующих механизмах аутентификации: [a-zA-Z]{3,15}:\/\/[^\/\\:@]+:[^\/\\:@]+@.{1,100}. Благодаря использованию такого шаблона можно обнаружить учётные данные, включённые в состав URL: protocol://username:password@example.com. Для поиска секретных данных в собственном коде нужно выяснить то, как устроены используемые в коде ключи, после чего — составить регулярные выражения для их поиска.

Благодаря двум вышеописанным стратегиям поиска секретных данных в коде можно обнаружить основной объём таких данных. Но, полагаясь только на поиск по текстам, мы рискуем пропустить те секретные данные, которые не представлены строками определённого формата. Тут нам на помощь может прийти энтропийный анализ кода.

Поговорим об энтропии


Мы можем воспринимать энтропию как показатель того, насколько «случайными» и «непредсказуемыми» являются некие данные. Например, у строки, составленной лишь из одного символа, вроде aaaaa, показатель энтропии очень низок. А вот строка, в состав которой входит большее количество различных символов, наподобие wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY, обладает более высоким показателем энтропии. Проверить подобные строки и узнать о том, как вычисляется показатель энтропии, можно, воспользовавшись этим калькулятором энтропии Шеннона.

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

Что дальше?


Код, отправленный в общедоступные репозитории, нужно проверять на предмет случайного попадания в него секретных данных. Если нечто подобное попало в общий доступ — это имеет смысл считать украденным, соответствующие ключи, пароли, и прочее подобное нужно поменять.

Конечно, не весь код является опенсорсным, не все секретные данные, жёстко заданные в коде, попадают в общедоступные репозитории. Но подобная практика, всё равно, может вылиться в проблему, так как эти данные могут покинуть пределы компании в исполняемых файлах приложений, в виде логов, при краже исходного кода. Хорошей стратегией по минимизации рисков утечек секретных данных является выполнение сканирования кода с использованием поиска по шаблону и энтропийного анализа данных. Делается это до попадания кода в продакшн. А сами секретные данные нужно хранить либо в конфигурационных файлах, либо — пользуясь специальными службами, предназначенными для управления такими данными.

Иногда может показаться, что секретные данные просто необходимо хранить в коде, который, в виде приложения, попадает к конечным пользователям. Например — речь может идти о ключах API, используемых в мобильных приложениях. В подобном случае можно принять меры по предотвращению возможности обнаружения подобных данных. Например, переменным, в которых хранятся некие ключи, лучше не давать имена, недвусмысленно указывающие на их содержимое, вроде api_key и password. Рекомендуется обфусцировать код, что усложнит извлечение из него секретных данных. И, наконец, можно просто выполнять ту часть кода приложения, которая отвечает за доступ к сторонним службам, на сервере, что позволит избежать необходимости включения этого кода в состав пакета приложения, передаваемого конечному пользователю.

Всегда проверяйте свой код на предмет наличия в нём секретных данных и исследуйте возможности попадания этих данных в руки злоумышленников. Если такие данные попадают в код не по случайности, подумайте о том, действительно ли они должны присутствовать в коде, и о том, достаточно ли хорошо они защищены.

В результате могу сказать, что статический анализ — это самый надёжный способ обнаружения секретных данных, которые случайно попали в код.

Выкладывали ли вы когда-нибудь в общий доступ код, в котором содержатся секретные данные?

oug5kh6sjydt9llengsiebnp40w.png

© Habrahabr.ru