Защитное преобразование и защита мастер-ключа с помощью парольных фраз

В корпоративной среде ноутбуки уже давно стали намного популярнее стационарных компьютеров, поскольку ноут можно легко взять с собой на встречу, в командировку или просто использовать для дистанционной работы из дома. Вместе с тем, мобильность этих устройств создает существенную угрозу утечки данных, поскольку при краже или утере ноутбука злоумышленники смогут получить доступ ко всем документам, с которыми работал сотрудник, и не только к документам.
Один из способов решения указанной проблемы для обеспечения безопасности инфраструктуры домена ALD Pro мы видим в использовании защитного преобразования данных на диске, поэтому я обратился к своему хорошему товарищу Кириллу @mkirya, эксперту по вопросам аутентификации и электронной подписи, чтобы максимально детально разобраться в этом вопросе. За помощь в подготовке материала и отладке скриптов респект моему коллеге Вадиму @NotMusk, старшему инженеру продуктовой команды ALD Pro, и Евгению @rtresme, руководителю отдела R&D Компании «Актив».
В этом цикле статей мы подробно рассмотрим технологию LUKS с позиции системного администрирования и способы защиты мастер-ключа, в том числе и с использованием алгоритмов ГОСТ Р 34.10-2012 на Рутокен ЭЦП 3.0. Материал прошел обсуждение в фокус-группе нашего сообщества ALD Proфессионалов и будет включен в содержание открытого курса по службе каталога ALD Pro. Возможности повысить квалификацию в объеме 16 академ. часов не обещаем, но вкусных буковок будет много.
Операционную систему Linux можно установить на диск с защитным преобразованием, что позволит избежать несанкционированного доступа к данным, даже если злоумышленник получит прямой физический доступ к устройству. По сути эта дополнительная мера защиты является аналогом того, что в Windows именуется Bitlocker. Большинство современных дистрибутивов Linux поддерживают такой сценарий развертывания из коробки, поэтому достаточно просто выбрать соответствующий пункт в мастере установки, как показано на рисунке 1.

При загрузке системы, которая установлена на диск с защитным преобразованием, будет появляться приглашение для ввода парольной фразы. Если нажать клавишу <Esc>, то унылое классическое окно терминала сменится графическим интерфейсом plymouth, но тема оформления, установленная по умолчанию, вряд ли вас впечатлит.

Приложение plymouth было названо в честь города Плимут, штат Массачусетс. Приложение запускается на ранней стадии процесса загрузки еще до монтирования корневой файловой системы и реализует графический интерфейс для взаимодействия с пользователем и отображения процесса загрузки. Текущую тему оформления plymouth можно узнать с помощью утилиты plymouth-set-default-theme:
localadmin@pc:~$ sudo plymouth-set-default-theme text
Список всех тем, доступных в системе, можно получить с помощью ключа -l (--list):
localadmin@pc:~$ sudo plymouth-set-default-theme --list details text tribar
Чтобы получить дополнительный набор тем, установим пакет plymouth-themes:
sudo apt install plymouth-themes
Список тем оформления, доступных после установки дополнительного пакета...
localadmin@pc:~$ sudo plymouth-set-default-theme --list bgrt details fade-in glow script solar spinfinity spinner text tribar
Установим по умолчанию тему glow из дополнительного комплекта. Команду нужно вызывать с ключом -R (--rebuild-initrd), чтобы была выполнена повторная сборка образа initrd.img:
sudo plymouth-set-default-theme glow --rebuild-initrd
Для того, чтобы графический интерфейс plymouth открывался сразу без нажатия клавиши <Esc>, добавим опцию splash в параметр GRUB_CMDLINE_LINUX_DEFAULT из файла /etc/default/grub:
sudo sed -i 's|"quiet|"splash quiet|' /etc/default/grub cat /etc/default/grub | grep quite
Результат выполнения команд...
localadmin@pc:~$ sudo sed -i 's/"quite/"splash quite/' /etc/default/grub localadmin@pc:~$ cat /etc/default/grub | grep quite GRUB_CMDLINE_LINUX_DEFAULT="splash quite net.ifnames=0 parsec.max_ilev=63"
Обновим конфигурацию GRUB, чтобы изменения вступили в силу:
sudo update-grub
Ну вот, теперь приглашение для ввода пароля стало намного привлекательнее (рис. 3).

Далее вы можете ознакомиться со скриншотами базовых тем оформления plymouth.
Тема bgrt...
Тема bgrt (англ. Boot Graphics Resource Table – таблица загрузки графических ресурсов) является вариацией темы spinner, которая отображает логотип производителя техники OEM, если он будет доступен.

Тема details...
Тема details не имеет графического интерфейса и выводит подробную информацию о ходе загрузки операционной системы в текстовом формате.


Тема fade-in...
Тема fade-in предлагает простой интерфейс, в котором загрузка выполняется на фоне мерцающих звезд и плавно загорающегося логотипа.


Тема glow...
Тема glow предлагает корпоративный стиль оформления с прогресс-баром в виде круговой диаграммы, за которым появляется светящийся логотип.


Тема script...
Тема script является примером для создания кастомизированного интерфейса загрузки с классическим прогресс-баром в виде горизонтальной полосы.

Тема solar...
Тема solar является космической темой с ярко-синей вспыхивающей звездой.

Тема spinfinity...
Тема spinfinity (англ. spin finity - вращающаяся бесконечность) предлагает простой интерфейс с индикатором загрузки в виде знака бесконечности в центре экрана.

Тема spinner...
Тема spinner предлагает простую тему оформления с круглым индикатором загрузки, который вращается в центре экрана.


Тема text...
Тема text предлагает интерфейс в виде текстовой строки в центре экрана с приглашением для ввода пароля. Индикатор загрузки представляет собой три точки, загорающиеся по очереди.


Тема tribar...
Тема tribar является вариацией темы text, которая использует прогресс-бар в виде трехцветной горизонтальной полосы в нижней части экрана.


После загрузки системы вы сможете узнать, какой именно диск использует защитное преобразование, например, с помощью утилиты blkid (block device id):
localadmin@pc-1:~$ sudo blkid --match-token TYPE=crypto_LUKS --output device /dev/sda5 localadmin@pc-1:~$ sudo blkid --match-token TYPE=crypto_LUKS --output value -s UUID 453fa939-a585-4e1f-a030-b11991a78d3f
, где:
-t (--match-token)— выполняет поиск устройства по значению параметра. К наиболее распространенным параметрам относятсяTYPE,LABELиUUID;-o (--output)— определяет формат вывода;-s (--match-tag)— определяет список тегов, значения которых нужно отобразить для найденного устройства.
Дополнительную информацию об организации хранения данных вы можете получить с помощью утилиты lsblk (list block devices):
localadmin@pc:~$ lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 32G 0 disk ├─sda1 8:1 0 487M 0 part /boot ├─sda2 8:2 0 1K 0 part └─sda5 8:5 0 31,5G 0 part └─sda5_crypt 253:0 0 31,5G 0 crypt ├─pc--vg-root 253:1 0 30,5G 0 lvm / └─pc--vg-swap_1 253:2 0 976M 0 lvm sr0 11:0 1 51M 0 rom
На базовом уровне механизм защитного преобразования опирается на стандартную подсистему DM-Crypt, которая появилась в ядре Linux еще с версии 2.6 и обеспечивает сопоставление устройств (Device Mapper, DM), плюс непосредственно защитное преобразование данных (Crypt). Поверх этой подсистемы работает технология LUKS, которая обеспечивает универсальный способ организации хранения ключей в разметке дисков Linux (Linux Unified Key Setup-on-disk-format). В соответствии с LUKS данные диска защищаются случайно сгенерированным мастер-ключом, который в свою очередь защищается уже ключом пользователя. С первого взгляда кажется немного запутанно, не правда ли?

Технология LUKS была разработана для возможности использования сразу нескольких пользовательских ключей или парольных фраз. Данные на диске защищаются мастер-ключом, значение которого обычно не меняют в течении всего времени использования диска, поскольку изменение мастер-ключа потребует повторного преобразования всех данных блочного устройства, что является очень ресурсоемкой операцией (см. команду cryptsetup reencrypt). Мастер-ключ шифруется пользовательским ключом и сохраняется в одном из слотов заголовка LUKS, что позволяет использовать несколько пользовательских ключей и быстро менять их при необходимости (рис. 4).

В первой версии LUKS заголовок диска содержал бинарный блок данных с метаинформацией и 8 слотов для хранения пользовательских ключей. В современных системах используется вторая версия LUKS, в которой к бинарному блоку добавлена область для хранения дополнительной метаинформации в формате JSON, количество слотов увеличено до 32, а весь заголовок продублирован дважды для обеспечения надежности. Структура заголовка LUKS2 представлена на рисунке 5.

Бинарный заголовок включает следующие поля:
magic— уникальная строка, определяющая сигнатуру LUKS;version— версия LUKS;hdr_size— размер бинарного заголовка LUKS вместе с областью для хранения JSON-объекта с дополнительной метаинформацией;sequid— счетчик изменений, который увеличивается каждый раз при обновлении заголовка LUKS. Система будет использовать резервный заголовок, если у него значение счетчика изменений окажется больше;label— текстовая метка в формате ASCII, которая может использоваться в udev-правилах;csum_alg— алгоритм для проверки контрольной суммы, которая покрывает как бинарный блок данных, так и последующую область JSON с дополнительной метаинформацией;salt— случайное значение, которое является уникальным для любой из копий заголовка, что позволяет исключить дедупликацию секторов, содержащих метаинформацию LUKS;uuid— уникальный идентификатор устройства, который может использоваться в udev-правилах;subsystem— дополнительная метка с именем подсистемы, которая является владельцем шифрованного раздела LUKS. Поле может использоваться в udev-правилах;hdr_offset— смещение заголовка от начала устройства (в байтах). Если смещение не совпадет с реальным положением заголовка на диске, то заголовок будет считаться некорректным;csum— контрольная сумма, рассчитанная с использованием алгоритма, который указан в поле csum_algorithm.
Например, размер заголовка hdr_size представляет собой целое число размером 8 байт и хранится в нулевом секторе со смещением также в 8 байт. Извлечь эту информацию можно с помощью утилиты od следующим образом:
sudo od --address-radix n --endian=big --format d8 --skip-bytes 8 --read-bytes 8 /dev/sda5
, где:
-A(--address-radix) — определяет формат для вывода информации о смещении относительно начала файла. Параметр может принимать четыре значения:d— смещение в числах десятичной системы счисления (decimal),o— смещение в числах восьмеричной системе счисления (Octal),h— смещение в числах шестнадцатеричной системе счисления (Hex),n— без информации о смещении (None);
--endian— определяет прямой или обратный порядок байт. Параметр может принимать два значения:big— порядок от старшего к младшему байту An-1 ... A0 (англ. big-endian — с большого конца),little— от младшего к старшему A0 ... An-1 (англ. little-endian — с малого конца);
-t(--format) — определяет формат, в котором нужно представить данные. Значениеd8соответствует целому числу из 8 байт в десятичной системе счисления;-j(--skip-bytes) — определяет количество байт, которые нужно пропустить от начала файла;-N(--read-bytes) — определяет количество байт, которые нужно вычитать из файла;/dev/sda5— определяет имя файла.
Результат выполнения команды покажет значение 16384:
localadmin@pc-1:~$ sudo od --address-radix n --endian=big --format d8 --skip-bytes 8 --read-bytes 8 /dev/sda5 16384
Область JSON включает следующие разделы:
config— содержит общие параметры заголовка, например, размер JSON-объекта, размер области ключей и др.;segments— объекты содержат информацию о зашифрованных областях данных, такую как размер кластера, алгоритм шифрования, значение вектора инициализации и др. На диске LUKS обычно находится только одна зашифрованная область, поэтому в заголовке описывается только один сегмент;keyslots— объекты содержат дополнительную метаинформацию о пользовательских ключах, такую как тип ключа, его размер, приоритет, параметры хеширования и алгоритма шифрования для извлечения мастер-ключа;digests— объекты содержат информацию для проверки извлеченного мастер-ключа. Каждому слоту должен быть назначен дайджест для проверки полученного значения;tokens— объекты содержат дополнительные метаданные, необходимые для получения пользовательского ключа с использованием внешних систем. Например, может содержать пользовательский ключ, зашифрованный открытым ключом USB-токена.
Прочитать JSON-заголовок можно с помощью утилиты dd с помощью следующей команды:
sudo dd if=/dev/sda5 bs=1 skip=4096 count=$((16384 - 4096))
, где:
if— определяет имя файла, из которого нужно вычитать информацию;bs— определяет размер блоков, с которыми будет работать утилита, значение указывается в количестве байт;skip— определяет количество блоков, которые нужно пропустить с начала файла. Заголовок JSON начинается с конца нулевого сектора размером 4096 байт;count— определяет количество блоков, которые нужно вычитать из файла. Размер JSON-заголовка можно рассчитать как разницу между размером всего заголовка метаданных hdr_size и размером одного кластера в 4096 байт, который выделяется под хранение бинарного заголовка данных.
Результат выполнения команды...
localadmin@pc-1:~$ sudo dd if=/dev/sda5 bs=1 skip=4096 count=$((16384 - 4096)) {"keyslots":{"0":{"type":"luks2","key_size":64,"af":{"type":"luks1","stripes":4000,"hash":"sha256"},"area":{"type":"raw","offset":"32768","size":"258048","encryption":"aes-xts-plain64","key_size":64},"kdf":{"type":"argon2i","time":4,"memory":587809,"cpus":4,"salt":"yY8WFQm4HOYgkm3Wu5NdptnGThVT4UV3FjL86SRceCM="}}},"tokens":{},"segments":{"0":{"type":"crypt","offset":"16777216","iv_tweak":"0","size":"dynamic","encryption":"aes-xts-plain64","sector_size":512}},"digests":{"0":{"type":"pbkdf2","keyslots":["0"],"segments":["0"],"hash":"sha256","iterations":169125,"salt":"ebZtI+Y44wWP9SI7itk6NkCLEx3FdDbTD3tIbabRTAI=","digest":"fUEFoCJ9XdtujdjQnlFOxRGgd7Pw+jNIwYQVz+17iRQ="}},"config":{"json_size":"12288","keyslots_size":"16744448"}} 12288+0 записей получено 12288+0 записей отправлено 12288 байт (12 kB, 12 KiB) скопирован, 0,0303519 s, 405 kB/s
Область ключей — это просто блоки данных (слоты), в которых находится мастер-ключ, зашифрованный ключом пользователя. Пользовательский ключ может представлять собой как бинарные данные, которые можно передать только в виде файла, так и обычную парольную фразу, которую можно ввести с клавиатуры. При этом никто не запрещает записать парольную фразу в файл, например, командой «echo -n 'Pa$$w0rd' > password.key», и использовать этот файл в качестве ключа.
Для управления ключами шифрованного диска LUKS можно использовать следующие команды утилиты cryptsetup:
luksAddKey <файл_устройства> [<файл_нового_ключа>] [--key-file=<файл_текущего_ключа>]Команда добавляет еще один ключ, хотя правильнее будет сказать, что она шифрует мастер-ключ диска новым ключом пользователя и сохраняет полученное значение в следующий свободный слот. Для того, чтобы утилита
cryptsetupcмогла извлечь значение мастер-ключа из заголовка LUKS, ей с помощью параметра--key-fileпередается один из ранее установленных пользовательских ключей. Если файл ключа не будет определен через параметр, то утилита в интерактивном режиме запросит парольную фразу вместо ключа.
luksChangeKey <файл_устройства> [<файл_нового_ключа>] [--key-file=<файл_заменяемого_ключа>]Команда заменяет существующий ключ. Заменяемый ключ может быть предоставлен как парольная фраза в интерактивном режиме или передан как файл через параметр
--key-file. Новый ключ может быть введен как парольная фраза в интерактивном режиме или прочитан из файла, имя которого можно передать через позиционный параметр.
luksRemoveKey <файл_устройства> [<файл_удаляемого_ключа>] [--key-file=<файл_удаляемого_ключа>]Команда удаляет ранее установленный ключ. Удаляемый ключ может быть предоставлен как парольная фраза в интерактивном режиме, а также его можно передать в виде файла через позиционный параметр или с помощью именованного параметра
--key-file.
luksKillSlot <файл_устройства> [<номер_слота>] [--key-file=<файл_текущего_ключа>]Команда позволяет удалить любой ключ по номеру слота, используя для авторизации любой другой пользовательский ключ, который можно предоставить как пароль в интерактивном режиме или как ключ через параметр
--key-file.
Добавим еще один пользовательский ключ к зашифрованному диску LUKS с помощью следующей команды:
sudo cryptsetup luksAddKey /dev/sda5
Утилита сначала запросит существующую парольную фразу, затем попросит два раза ввести новый пароль:
localadmin@pc-1:~$ sudo cryptsetup luksAddKey /dev/sda5 Введите любую существующую парольную фразу: Введите новую парольную фразу для слота ключа: Парольная фраза повторно:
Заголовок шифрованного диска и список установленных пользовательских ключей можно посмотреть с помощью команды luksDump утилиты cryptsetup, см. раздел Keyslots.
Результат выполнения команды luksDump...
localadmin@pc-1:~$ sudo cryptsetup luksDump /dev/sda5 LUKS header information Version: 2 Epoch: 4 Metadata area: 16384 [bytes] Keyslots area: 16744448 [bytes] UUID: 453fa939-a585-4e1f-a030-b11991a78d3f Label: (no label) Subsystem: (no subsystem) Flags: (no flags) Data segments: 0: crypt offset: 16777216 [bytes] length: (whole device) cipher: aes-xts-plain64 sector: 512 [bytes] Keyslots: 0: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 4 Memory: 587809 Threads: 4 Salt: c9 8f 16 15 09 b8 1c e6 20 92 6d d6 bb 93 5d a6 d9 c6 4e 15 53 e1 45 77 16 32 fc e9 24 5c 78 23 AF stripes: 4000 AF hash: sha256 Area offset:32768 [bytes] Area length:258048 [bytes] Digest ID: 0 1: luks2 Key: 512 bits Priority: normal Cipher: aes-xts-plain64 Cipher key: 512 bits PBKDF: argon2i Time cost: 7 Memory: 1048576 Threads: 4 Salt: 3a 52 dc c0 0f 82 6f ac 3a a4 e9 74 7a 0f 6e 3d b8 54 af c3 7c 7a 3a dc bd 6a ec 90 5c 59 7a ee AF stripes: 4000 AF hash: sha256 Area offset:290816 [bytes] Area length:258048 [bytes] Digest ID: 0 Tokens: Digests: 0: pbkdf2 Hash: sha256 Iterations: 169125 Salt: 79 b6 6d 23 e6 38 e3 05 8f f5 22 3b 8a d9 3a 36 40 8b 13 1d c5 74 36 d3 0f 7b 48 6d a6 d1 4c 02 Digest: 7d 41 05 a0 22 7d 5d db 6e 8d d8 d0 9e 51 4e c5 11 a0 77 b3 f0 fa 33 48 c1 84 15 cf ed 7b 89 14
Очевидно, что энтропия парольной фразы будет намного ниже энтропии мастер-ключа, поэтому злоумышленник, похитив диск, сможет предпринять попытку взлома методом перебора. Чтобы сделать перебор нецелесообразным, мастер-ключ шифруется не самим паролем, а его ресурсоемким хешем. По умолчанию утилита cryptsetup хеширует пароли столько раз, чтобы для преобразования одной фразы с учетом текущей производительности компьютера требовалось не менее одной секунды, поэтому количество итераций в LUKS называют также стоимостью по времени (time cost). Порядок извлечения мастер-ключа отражен на рисунке 6.

Но одной только стоимости хеширования по времени недостаточно, чтобы надежно защитить систему от взлома. Если использовать простые цифровые PIN-коды, то для подбора 6-значного числа потребуется всего 106 попыток, что займет не более 12 дней. Сложность взлома можно увеличить, указав вручную количество итераций с помощью параметра --pbkdf-force-iterations (или определив желаемую стоимость хеширования по времени в количестве миллисекунд с помощью параметра --iter-time, чтобы утилита cryptsetup сама рассчитала необходимое количество итераций). Но даже если поднять стоимость в 100 раз, использование цифровых PIN-кодов все равно не станет безопасным, а пользовательский опыт безнадежно ухудшится (вряд ли пользователь скажет нам спасибо, если загрузка компьютера станет дольше на две минуты).

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