October 19, 2020

Проблемы доверия. Как разделить ключ между людьми и застраховаться от его потери

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

Секреты бывают разными

Пароли, API-ключи, сертификаты и другие секреты неравнозначны. Для любой компании риск несанкционированного доступа можно выразить в прямых и косвенных убытках. Если сумму убытков от утечки секрета умножить на вероятность атаки, то можно разбить наши пароли на несколько категорий:

  • Никому не нужные. Поднял виртуальную машину, что-то потестировал и забыл. Максимум, что можно утащить, — расписание полива растений в офисе из скрипта одного админа.
  • Потенциально значимые. Тестовая БД, какой-то второстепенный сервер или что-то подобное. Несанкционированный доступ сам по себе не принесет убытков, но может быть «трамплином» для более глубокого проникновения в инфраструктуру компании.
  • Значимые. Боевая БД, важный сервер логов и другие ключевые системы. Если злоумышленник проникнет сюда, то сможет либо выкрасть ценную информацию, либо значительно нарушить работу компании.
  • Офигенно критичные. Что может быть более неприятным, чем скомпрометировать важный пароль? Правильно — продолбать всю связку паролей целиком. Например, базу Keepass вместе с ключом для доступа. Если злоумышленник до нее доберется, то финансовый ущерб компании может быть невосполнимым.

Мы поговорим о последней категории, которую нужно защищать не только от атак извне, но и от внутренних угроз.


У всех есть своя цена

К сожалению, не все люди честные и правильные. Видел новости про утекшие базы данных, которые сливали сотрудники? А это, между прочим, вполне себе статья УК. Давай заглянем в голову к таким сотрудникам.

Человек из регионального отделения получает зарплату в условные 35 тысяч рублей. Ему предоставили доступ к важной базе, чтобы он мог выполнять свои рабочие задачи. Совершенно внезапно к нему из глубин даркнета приходит заманчивое предложение слить всю базу за 500 тысяч рублей. Сотрудник смотрит в свою зарплатную ведомость, оценивает свои шансы быть пойманным и идет на этот риск.

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

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

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


Красная кнопка для генерала

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

Вполне разумным вариантом будет выдать ключи для запуска нескольким людям. Например, дежурному офицеру и начальнику секретной базы с МБР. Таким образом, внезапно сошедший с ума офицер не сможет устроить третью мировую войну, приняв решение единолично. Мы снижаем вероятность несанкционированного доступа, разделяя секрет между ключевыми людьми.

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


Оптимальная защита

Хочу сразу заметить, что очень сложно найти хороший баланс между удобством шифрования секретной информации и надежностью. Любой вариант «резервных кодов доступа» на случай, если основные будут утрачены, ослабляет защиту и добавляет дополнительные векторы атаки. Если мы пытаемся разделить секрет на несколько ответственных, то все становится еще сложнее.


Сейф и бумажки

Допустим, мы защищаем условный суперпривилегированный секрет, который может использоваться только в исключительных случаях. Например, мобильные телефоны, имеющие доступ к корпоративной почте и ресурсам, управляются с помощью системы MDM (Mobile device management). Мы не хотим, чтобы кто-то один из подразделения ИБ мог получить доступ к данным с телефона сотрудника. При этом нам нужно иметь возможность дистанционно уничтожить содержимое телефона или узнать его текущие координаты GPS, если устройство украдут. Соответственно, нам нужно решение, которое позволит разделить ответственность между несколькими людьми.

Мы можем напечатать пароль на листочке, положить в конверт, залить сургучом и торжественно положить в сейф. Уже неплохо. Вскрывать и опечатывать будем только в присутствии комиссии. Но листочек сложно бэкапить, он хранит секрет в открытой форме, что увеличивает риски его компрометации. А еще это физический объект — если кто-то из обязательных членов комиссии находится в командировке, экстренный доступ становится проблемным.


Матрешка с паролями

К черту бумажки и картонные папочки. Будем современными. Давай пойдем самым простым путем и сделаем архив 7-Zip, зашифрованный криптостойким AES-256. Мы не хотим, чтобы один сотрудник мог единолично получить доступ к секрету, поэтому мы будем конструировать матрешку из вложенных архивов, где каждый слой закрывает своим паролем новый человек. Например, руководитель ИБ и технический директор.

На первый взгляд, все работает отлично. Надежность защиты от компрометации быстро растет пропорционально числу людей. Например, если вероятность утечки пароля от одного человека — 0,05, то для шести человек уже 0,056 = 1,5625 × 10–8.

Считаем по формуле

Круто. Но есть проблема. Вероятность необратимо утратить защищаемый секрет точно так же нарастает. C человеком нередко случается какая-нибудь фигня. Шагнет, например, неудачно под автобус на красный свет, или склероз нападет. Если это централизованное хранилище особо ценных для компании данных, их утрата может быть фатальной.


Разбиваем на фрагменты

На самом деле есть хорошее решение.

Существует очень изящная реализация разделения секрета на несколько частей — схема Шамира. Да, это тот самый Ади Шамир, который S в аббревиатуре RSA. При использовании этого метода исходный пароль разбивается на k равнозначных частей. Особенность схемы в том, что для восстановления секрета требуется лишь определенная часть фрагментов. Например, любые четыре из шести. При этом если тебе известны три части из шести, то это никак не поможет восстановить изначальный пароль.

Размер одного фрагмента при этом равен исходному секрету, поэтому, как и в криптографии с открытым ключом, обычно нет смысла дробить на части большой объем данных. Гораздо проще нарезать ломтиками ключ от быстрого симметричного алгоритма шифрования и уже им зашифровать весь объем данных. Этот метод хорошо масштабируется. Можно добавлять новых людей, которые хранят части общего ключа. При этом размеры кворума не изменятся. То есть если раньше надо было собрать три ключа из пяти, то теперь достаточно трех из восьми.

А еще есть возможность ротации фрагментов ключей. Алгоритм подразумевает схему, при которой достаточное число людей собираются и генерируют новый набор. Зашифрованный общий ключ остается неизменным. Это очень ценное свойство на случай компрометации, увольнения сотрудника или других проблем.

Самое главное, что он подразумевает большую гибкость при раздаче частей ключа. Например, генеральному директору можно выдать три фрагмента, а всем остальным по одному. Таким образом можно учитывать степень важности и благонадежности каждого ответственного человека в компании.


DRP на людей

DRP (Disaster Recovery Plan) — это такая важная штука, которая в идеале никогда не должна пригодиться. Но уж если случается фигня, то ты с умным видом открываешь ящик, листаешь папки с надписями «Падение продуктивной БД», «Пожар в дата-центре», «Утечка Т-вируса» и, наконец, берешь «Внезапный день рождения руководителя». После этого просто следуешь заранее разработанной инструкции. И в принципе, совершенно неважно, что именно произошло, но у тебя уже есть заранее спрогнозированные карты ущерба, планы аварийной миграции кластера на другую площадку и тому подобное.

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

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

А теперь представь себе тот самый «фактор автобуса». Во время очередного корпоратива трое из Хранителей внезапно выбывают навсегда. Неважно, лодка опрокинулась или кто-то неудачно пожарил только что собранные грибы. Теперь у тебя на руках есть три ключа и потерянная навсегда база с криптостойким шифром.

Что с этим делать? Хранители должны быть географически распределены и не встречаться одновременно в одной точке. Это не так сложно реализовать, если у тебя крупная компания с множеством филиалов. Часть Хранителей может быть в «холодном резерве» и не использоваться в обычных процессах. Да, тут работают все те же принципы геораспределенности и резервирования, что и при построении классических отказоустойчивых архитектур.

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


Hashicorp Vault

Давай перейдем к более практическим примерам. Есть такой замечательный продукт — Vault компании Hashicorp. Это когда ты вместо того, чтобы расклеивать админские пароли на бумажках и хардкодить их прямо в скриптах, создаешь централизованное виртуальное хранилище, обнесенное колючей проволокой и горящими крокодилами. Все доступы распределены, каждый запрос через API логируется, и все это отлично интегрируется с процессами CI-CD и автоматизированными системами.

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

Самое интересное в другом: если у нас БД хранится в зашифрованном виде, то нодам Vault, чтобы предоставлять доступ к паролям, нужно хранить у себя мастер-ключ для доступа к базе. Это проблема, так как они сразу же станут первоочередным объектом для атаки. Малейшая уязвимость ноды, копия виртуальной машины недобросовестным админом, и мастер-ключ утек. Вместе со всеми секретами компании.

И вот тут появляется интересная особенность Vault. Он не хранит пароль нигде на жестком диске, своп отключен, и появление мастер-ключа где-то, помимо оперативной памяти, исключено. После перезагрузки нода понятия не имеет, как открыть зашифрованную базу, и требует поочередного ввода фрагментов Шамира кворумом Хранителей. Реализуется это либо через консоль SSH, либо через веб-интерфейс, что очень упрощает разблокировку ноды в случае распределенной команды.

Что получается в результате? Из фрагментов Шамира собирается мастер-ключ, им расшифровывается уже ключ, которым зашифрована база данных. Двухступенчатость нужна для того, чтобы иметь возможность регулярно ротировать фрагменты Шамира и изменять их число без необходимости перешифровывать всю базу. Если посмотреть на процесс абстрактно, то мы формируем пароль, используя кворум неизвлекаемых хранилищ — содержимого памяти нескольких людей. В результате формируется такой же неизвлекаемый в большинстве случаев мастер-ключ, хранящийся в RAM.


Проблема первичной раздачи

Самая сложная часть в процедуре первичной выдачи ключей.

  1. Ты должен обеспечить возможность удаленной выдачи фрагментов Шамира.
  2. Не должно быть человека, который нажмет что-то в консоли, получит все фрагменты и разошлет всем остальным.

$ vault operator init

Unseal Key 1: 4jYbl2CBIv6SpkKj6Hos9iD32k5RfGkLzlosrrq/JgOm

Unseal Key 2: B05G1DRtfYckFV5BbdBvXq0wkK5HFqB9g2jcDmNfTQiS

Unseal Key 3: Arig0N9rN9ezkTRo7qTB7gsIZDaonOcc53EHo83F5chA

Unseal Key 4: 0cZE0C/gEk3YHaKjIWxhyyfs8REhqkRW/CSXTnmTilv+

Unseal Key 5: fYhZOseRgzxmJCmIqUdxEm9C3jB5Q27AowER9w4FC2Ck

Есть возможность инициализировать его из консоли единолично, как в примере выше. Потом админа можно смело сталкивать с утеса. Но есть и более гуманные механизмы. Каждый из Хранителей предоставляет открытую часть своего GPG-ключа админу Vault. После чего происходит инициализация:

$ vault operator init -key-shares=3 -key-threshold=2

-pgp-keys="jeff.asc,vishal.asc,seth.asc"

Key 1: wcBMA37rwGt6FS1VAQgAk1q8XQh6yc...

Key 2: wcBMA0wwnMXgRzYYAQgAavqbTCxZGD...

Key 3: wcFMA2DjqDb4YhTAARAAeTFyYxPmUd...

...

Расшифровать каждый фрагмент Шамира сможет только обладатель закрытой части GPG. Мы успешно распределили секрет, ни разу не собрав его в одном месте в открытом виде.


Выводы

  1. Проектируй любую систему со здравой порцией паранойи.
  2. Никогда не забывай, что мало все тщательно зашифровать. Важно суметь расшифровать это все обратно.
  3. Если при потере ключей остановится бизнес, будь вдвойне внимателен.
  4. Оставляй бэкдор в виде бумаги в сейфе.
  5. Люди — тоже часть механизма, и их нужно резервировать. Резервируй.

И никому не доверяй.