Секреты: ключи, хранение и ротация
Почему ключ в git — это ключ, которым ты больше не владеешь, от чего на самом деле защищают переменные окружения, как работает ротация до и после утечки и честный подсчёт того, с чего начинаются утечки на самом деле.
Прошлый урок закончился паролем. Пароли — это секреты, которые вводит человек; этот урок о секретах, которыми пользуется программа, когда никто не смотрит: ключи API, пароли к базам, ключи подписи, токены к другим сервисам. Ломаются они иначе, чем человеческие пароли. Никто не переиспользует пароль от базы из лени, никто его не забывает и никто не выбирает слабый, потому что так проще набирать. Они ломаются потому, что их копируют туда, где им никогда не следовало лежать, и потому что живут они вечно.
Секрет, закоммиченный в репозиторий, — это не файл, который можно удалить. Git хранит снимки, и каждый снимок, в котором ключ когда-либо был, содержит его до сих пор. Удаление строки в новом коммите убирает ключ из текущего состояния и ничего не меняет в истории. Даже force-push, переписывающий ветку, оставляет старые объекты достижимыми через reflog, через любой форк, через любой уже сделанный клон и какое-то время через хранилище самого провайдера. Это не изъян Git; это то, для чего существует история, адресуемая по содержимому. Практический вывод — тот, принять который командам дороже всего: к моменту, когда ты заметил, учётные данные надо считать публичными. Как найти утёкший ключ в истории и убедиться, что он всё ещё достижим. Поиск — это лёгкая часть.
$ git log --all -S 'AKIA' --oneline
9f2c1ab remove keys from config
4d7e0f3 add deployment config
$ git show 4d7e0f3:config/prod.env | grep -c AKIA
1
$ git rev-list --all --count
8421
# the commit is still reachable: history is append-onlyКак найти утёкший ключ в истории и убедиться, что он всё ещё достижим. Поиск — это лёгкая часть.Переменные окружения — стандартный совет, и это шаг вперёд, но их часто переоценивают. Что они действительно дают: секрет не в репозитории, не в образе контейнера и отличается между стендом и продом без правки кода. Чего они не дают: никакой изоляции вообще. В Linux окружение работающего процесса читается из /proc, поэтому ключ может прочитать любой процесс, работающий от того же пользователя. Он попадает в дампы падений и в дочерние процессы, которые ты порождаешь, — именно так ключ от платёжного сервиса всплывает в логе несвязанной задачи. А на многих платформах он виден на странице настроек в панели, то есть виден всем, у кого есть право деплоя. Переменные окружения переносят секрет из плохого места в лучшее. Они не сейф. Переменную окружения прочитает всё, что уже может работать от имени этого пользователя. Это не ничего, но это не секретность. Вывод ключа — это мост от человеческого секрета к криптографическому ключу. Пароль не используется как ключ AES напрямую, потому что пароли короткие и низкоэнтропийные, а ключи должны быть равномерно случайными. Функция вывода берёт пароль, соль и намеренно дорогой параметр стоимости и выдаёт ключ нужной длины. Та же идея встречается в трёх местах сразу, и стоит увидеть, что это одна и та же идея: вывод ключа из парольной фразы, вывод хеша пароля, устойчивого к перебору, и вывод отдельного ключа на запись из мастер-ключа, чтобы утечка одного ключа не открывала остальные. Именно параметр стоимости заставляет каждую из них сопротивляться атакующему, у которого уже есть сохранённый материал. У шифрования диска есть характерный режим отказа, который стоит назвать. Если ключ, отпирающий диск, лежит на той же машине или автоматически подтягивается при загрузке из файла конфигурации, то данные есть у любого, кто может загрузить эту машину или прочитать этот файл. Поэтому полнодисковое шифрование защищает украденный ноутбук и почти ничего не даёт для работающего сервера, до которого атакующий уже добрался.
in transit TLS between client and service a key you never see
at rest disk encryption, KMS envelope server holds the key
tokenized provider keeps the card number your database holds a token
PCI DSS 4.0 requires cardholder data encrypted in transit and at rest
None of the three protects against a valid account using the API.Три слоя, которые имеют в виду, когда говорят «данные зашифрованы». Они защищают от разного, и ни один не защищает от действительного запроса.Практика
Ты обнаруживаешь, что рабочий ключ API закоммитили три месяца назад и удалили следующим коммитом. Ключ всё ещё активен. Какая первая реакция верна?
Подсказки
- Спрашивать, лежит ли файл до сих пор в рабочем каталоге, — не тот вопрос: коммит хранит собственную копию каждого файла, которого когда-либо касался.
- Прежде чем что-то переписывать, реши, действительны ли ещё эти учётные данные. Если да, уборка — не срочная часть.
Расставь реакцию на утечку учётных данных по порядку, от первого действия к последнему.
- 1выпустить замену и развести её по всем потребителям старого ключа
- 2переписать историю или сознательно принять, что она останется, и добавить сканирование в конвейер, чтобы следующую утечку поймать до слияния
- 3просмотреть логи на все случаи использования старого ключа, вплоть до момента коммита
- 4записать, что произошло и что поймало бы это раньше, пока детали ещё свежи
- 5отозвать учётные данные у провайдера, чтобы они немедленно перестали работать
Подсказки
- Пока утёкшие учётные данные действительны, всё остальное — украшение. Отзыв идёт первым, а не последним.
- Чистить репозиторий имеет смысл только после того, как учётные данные больше нельзя использовать, потому что переписывание истории — дело долгое и разрушительное.
Сопоставь каждое место, где может жить секрет, с тем, от чего оно действительно защищает.
Подсказки
- Для каждого варианта хранения спроси, кто может его прочитать: сам процесс, любой, кто может читать файловую систему, или только тот, кто прошёл аутентификацию.
- Переменные окружения держат секреты вне репозитория, и это настоящая ценность. Но от всего, что работает на той же машине, они секрет не прячут.
Вопросы по этому уроку
Это бесплатно?
Да. Первый уровень каждого предмета бесплатный и не требует аккаунта. Дальше уровни и старшие грейды платные — именно это позволяет проекту существовать.
На какой возраст рассчитано?
Для любознательных подростков примерно с 12 лет и для взрослых, начинающих с нуля. Опыт программирования не предполагается: уроки по сетям и физике не требуют математики сверх школьной.
Нужно ли уже уметь программировать?
Нет. Логика, физика и сети начинаются с нуля. Код ты пишешь в разделе языков — и он тоже начинается с самого начала.
Нужно что-то устанавливать?
Нет. Всё работает в браузере: теория, практика и интерактивные симуляции. Скачивать нечего и настраивать нечего.
Сколько времени занимает один предмет?
Урок — от 10 до 15 минут. Грейд «джун» это примерно 12–20 уроков, то есть несколько недель регулярных занятий, а не один вечер.
Обязательно идти по порядку?
Внутри грейда уровни открываются по очереди: каждый опирается на предыдущий. А между предметами ты свободен — начинай с логики, физики или сетей.