Ресурсы: техническое описание TLS, LaTeX - в картинки (img), криптографическая библиотека Arduino, шифр "Кузнечик" на ассемблере AMD64/AVX и ARM64
Небольшое продолжение записки про извлечение секретных криптографических ключей из дампов памяти (оперативной). Ключи в памяти могут быть замаскированы – тогда не работает простой и универсальный метод, основанный на полном прямом переборе последовательностей байтов с пробным расшифрованием или с пробным вычислением открытого ключа асимметричной криптосистемы. То есть, предположим, что ключ ECDSA разрядности 256 бит находится в памяти в виде записанных последовательно 32 байтов. При этом известен открытый ключ, соответствующий искомому секретному. Тогда можно последовательно двигаться по дампу памяти, выбирать очередные 32 байта, вычислять открытый ключ и сравнивать его с проверочным. Если значения совпали, то секретный ключ найден. Это и есть прямой перебор. Вычисление открытого ключа ECDSA – достаточно быстрая процедура, так что проверка нескольких гигабайтов памяти даже на обычном настольном ПК не займёт много времени.
Самый простой способ маскирования, который тут же приходит на ум, состоит в суммировании байтов ключа (XOR) с некоторыми байтами маски, маска сохраняется в памяти отдельно. Если в дамп попал такой замаскированный ключ, то проверять потребуется уже гораздо больше данных: очередную последовательность тестируемых байтов, которая может быть записью ключа, нужно предварительно суммировать XOR с другими последовательностями байтов соответствующей длины из дампа, и только потом проверять, не ключ ли получился в результате.
Интересно, что далеко не всегда секретный ключ должен быть раскрыт в памяти полностью перед его использованием штатным кодом. Дело в том, что маскирование можно устроить существенно более сложным образом. Пример: пусть в рамках протокола точка эллиптической кривой умножается на секретный скаляр (как в протоколе Диффи-Хеллмана), тогда в процессе быстрого умножения секретный ключ используется по отдельным битам, соответственно, можно только отдельные биты и раскрывать, на каждом шаге алгоритма. Естественно, если биты маски берутся из заранее заготовленного массива, представленного в памяти последовательностью, то это не отличается от простого маскирования и раскрывается не менее простым пробным суммированием.
Однако можно придумать экзотические методы индексирования битов маски. Так, эти биты могут выбираться псевдослучайным образом из гораздо более длинного блока данных, сама же индексирующая последовательность сохраняется в виде программного кода (это, предположим, набор адресов переходов, помещаемый в стек). Тогда извлечение ключа из дампа памяти потребует, как минимум, обнаружения и анализа индексирующей последовательности (а в совсем уж экзотическом варианте – анализа программного кода, который может быть сделан мутирующим). Это всё весьма академические методы, поскольку они не только слишком сложны в реализации, но ещё и сложность не компенсируется эффектом от применения.
Постепенное раскрытие ключей при штатной работе подходит и для симметричных шифров. Например, в AES можно замаскировать при помощи XOR основной ключ и раскрывать его частично в ходе вычисления ключей раундов, которые, соответственно, тоже маскируются по мере разворачивания, при этом маска подмешивается в реализацию основного преобразования шифра, уже к ключам раундов.
Защита ключей в памяти важна и используется, но сложности, подобные описанным только что, вряд ли имеют первоочередное значение в ситуации доступа к виртуальной машине из гипервизора, как в исходной записке. Тем не менее, некоторые варианты защиты со сложными алгоритмами вполне полезны для аппаратных устройств, где маскирующее значение, например, может находиться в отдельном модуле памяти, который сложнее прочитать, чем оперативную память.
Комментировать »
Кстати, что касается “обезличивания” (или “анонимизации”) различных данных о персонах – есть хороший способ понять, в какой степени хорош тот или иной метод “обезличивания”: нужно попробовать представить, позволяют ли “обезличенные” данные различать неизвестных людей, если таковые собрались в некоторой комнате, в достаточно большом количестве. Под возможностью различать здесь подразумевается возможность точно сказать, что тот или иной фрагмент “обезличенных” данных относится к каким-то людям из присутствующих в комнате, а к каким-то – не относится. И чем выше оказывается различительная способность, тем хуже данные были обезличены (многие типы данных обезличить очень и очень сложно). Этот мысленный эксперимент хорошо показывает всякие неочевидные особенности.
Пример: есть база данных (БД), в которой записан рост людей из комнаты, с точностью плюс/минус пять сантиметров; хорошо ли эти данные помогают различать людей в рамках воображаемой комнаты? Вообще, разнообразие роста велико, но есть некоторый интервал, в который попадает очень много людей. Поэтому, в комнате окажется много людей как бы одинакового роста, в терминах нашей БД, тем более, что рост, по условиям, укладывается в плюс/минус пять сантиметров. Однако, если в комнате присутствует человек ростом 205 сантиметров, то, если это не встреча баскетболистов, скорее всего, персону нетрудно будет связать с записью из базы – интервал от 200 до 210 сантиметров не слишком-то заселён. Теперь, если в базе присутствовали данные о росте (плюс/минус – для “обезличивания”) и, скажем, фамилия, имя и отчество, то можно будет подойти, и к человеку высокого роста обратиться точно по имени. (Понятно, что знание ФИО вообще позволяет устроить перекличку, но Константинов Каретиных, предположим, может оказаться несколько.)
Другой пример: есть запись голоса. Этот вариант точнее, поскольку голоса не только обычно различаются (исключения – редки), но и это различие очень велико, особенно, если сравнение выполняет человек с хорошим, подходящим слухом. Голос работает не хуже фотографии лица (“анфас и профиль”). А по такой фотографии – различить людей в комнате совсем нетрудно. Однако биометрические данные – это одно, а всякие “цифровые следы” – другое.
Предположим, известны номера мобильного телефона. Насколько это точные данные? В рамках нашего мысленного эксперимента позвоним по выбранному номеру и будем наблюдать, у кого именно из присутствующих смартфон зазвонит. То есть, точность очень высока (конечно, при прочих допущениях: телефоны должны быть у всех присутствующих, должны быть включены и пр.) А вот только по домашнему адресу – различить людей в комнате довольно сложно, но ведь и адреса, сами по себе, не только всем известны (нанесены на карты), так ещё и никак не “персонализируются”, в отличие от телефонного номера смартфона, который буквально к человеку-носителю привязан.
Понятно, что различительная способность резко возрастает, когда к одним данным добавляются другие.
Данный мысленный эксперимент не является универсальным, но неплохо работает и для других видов данных, вплоть до сведений о покупке деловых костюмов, – попробуйте сами.
Комментировать »
Volkswagen собирается ChatGPT добавить к системам автомобиля. То есть, внутри автомобиля появится очередной “жучок”, прослушивающий и отправляющий разговоры на внешний сервер. Тут нетрудно предположить, что может оказаться внедрена и такая недокументированная функция, которая позволит отправлять команды автомобилю с серверов ChatGPT. Конечно, это будет особенность, внесённая случайно по ошибке (но без всякого противоречия “политике использования автомобиля”) – поскольку уже заранее официально утверждается, что доступа “к системам автомобиля” у ChatGPT не будет (однако, это же ведь “невиданный искусственный интеллект”, который может вот-вот выйти из-под контроля, как утверждается в СМИ, в пресс-релизах и в открытых письмах – казалось бы, что может пойти не так?). С другой стороны, в автомобили уже не только звукозаписывающую аппаратуру штатно устанавливают (до идеи с ChatGPT, понятно), но и видеокамеры, передающие данные на центральные серверы, так что решение нельзя назвать радикально новым.
Комментировать »
Снова про “квантовую криптографию”, которую, якобы, “невозможно взломать”. Несмотря на публикации в СМИ, предлагается-то некоторый технический канал для передачи секретных ключей, устройство которого включает в себя сигнализацию о том, что передаваемый ключ кто-то мог перехватить и подслушать – квантовое распределение ключей. Это, вообще говоря, эквивалентно не столько оптоволоконной линии с сигнализацией о нарушении, сколько защищённому конверту (ну или металлическому контейнеру), пусть и работает без “механической” части. Конечно, переносить какие-то артефакты вручную не нужно, однако только после того, как перенесено и налажено сложное оконечное оборудование, которое и подключить можно с ошибкой, и внутри оно может содержать дополнительные дефекты и “особенности”. Так что схема с металлическим контейнером может ещё и оказаться гораздо более предсказуемой.
То, что секретные ключи переданы при помощи “квантового канала”, даже не означает, что и для защиты передаваемых данных применяется абсолютно стойкий шифр: такой метод, конечно, необходим для обеспечения максимальной стойкости, но эффективный вариант всё равно использует обычный симметричный шифр. Заметьте, что квантовая криптография требует аутентификации сторон канала “классическими” методами, иначе теряется смысл. Более того, так как речь исключительно о технической защите, то оценку защищённости нельзя смешивать с криптографической стойкостью (хоть в названии и использовано слово “криптография”): примерно так же, как нельзя оценивать защищённость помещения только и исключительно по количеству возможных комбинаций механического дверного замка.
Комментировать »
Добавил arm64 в свою реализацию шифра “Кузнечик” (ГОСТ Р 34.12-2015) на ассемблере для Go. Новые функции – для платформы arm64 со 128-битной арифметикой. Попутно немного изменил состав библиотеки, так что название теперь новое (см. исходники ниже). Раньше ассемблер был только для amd64 с AVX, на остальных платформах – компилировалось с “заглушкой”, в которой операции шифра реализованы просто на конструкциях Go – ассемблер, понятно, быстрее.
ARM – весьма и весьма распространённая архитектура, а использование ассемблера, как обычно, даёт прирост производительности во много раз, если сравнивать с “обычным” Go-шным вариантом (тоже оптимизированным, конечно). Однако я всё проверял на Raspberry Pi4 (RPi4), потому что другого варианта ARM-аппаратуры под рукой сейчас не оказалось (а техники Apple у меня вообще нет). Зато на Linux/arm64 в RPi4 – прирост более чем в двадцать раз. Было: примерно мегабайт в секунду для “обычной” реализации. Стало: около 23 мегабайтов в секунду для ассемблерной. Думаю, что должно работать и на других ARM-платформах, и гораздо быстрее, чем на RPi4, но пока не проверял: если кто-то использует, и обнаружит существенные несовместимости (см. ниже) – пишите, пожалуйста, в комментарии здесь или пишите почтой. Вообще, написанный ARM-код ещё есть куда оптимизировать: например, в arm64 имеется “векторная” инструкция, реализующая XOR сразу для трёх входных регистров, что тут очень полезно, но эта инструкция недоступна в процессоре RPi4 (Cortex-A72/BCM2711).
Новая версия кода “Кузнечика”, так как отличается от предыдущей, называется GOSThp. Исходный код публикую ниже. Возможно, потом оформлю в виде полноценного модуля для Golang и выложу копию на Github, а возможно – не оформлю и не выложу. Но использовать в составе проекта на Go этот пакет нетрудно: достаточно скопировать директорию gosthp (см. ещё детали ниже). Работает со старыми версиями Go (проверено для 1.7).
Файлы:
gosthp.tar.gz – всё вместе в одном архиве.
SHA-256: b3f860f15d43814db2f2becfb4c72272de6d3b49f9b0dec70712438af0a6e085
(Update, 20/01/2024: архив перезагружен – изменено описание платформ в +build, даты в комментариях, и даты создания файлов; код – без изменений.)
Внутри архива:
cmd/ | gostrun.go - файл с примерами и тестами. gosthp/ - это сама библиотека. | docipher_amd64.go - объявления функций для amd64. | docipher_amd64.s - код на ассемблере для amd64. | docipher_arm64.go - объявления функций для arm64. | docipher_arm64.s - код на ассемблере для arm64. | docipher.go - универсальная реализация для прочих платформ (см. +build внутри: могут быть расхождения). | gosthp.go - основной модуль. go.mod README
Основное дополнение – docipher_arm64.s, собственно, реализация шифра для архитектуры arm64/AArch64.
Как обычно, прилагается демонстрационный код – gostrun.go. Если развернуть упомянутый выше архив в директорию, то собрать gostrun можно так:
$ cd cmd $ go build -o ~/my-builds/gostrun gostrun.go
Go самостоятельно выберет нужные файлы в соответствии с целевой платформой. Внутри исходников – много комментариев (англ.).
Саму библиотеку можно использовать в среде Go в составе других проектов простым копированием gosthp/, разместив её либо в директорию сборки, либо в общую директорию для пакетов.
Ассемблерная реализация для ARM от x86 отличается в некоторых деталях, не алгоритмических: во-первых, очевидно, другие инструкции; во-вторых, используется больше регистров и основные операции XOR собраны в общий блок; в-третьих, изменён способ вычисления смещений для обращения к таблице значений.

Как обычно, основная оптимизация – использование предвычисленной таблицы. На ассемблере написаны только низкоуровневые операции шифра, то есть, преобразование блока с таблицей и раундовыми ключами. Библиотека позволяет создать экземпляр объекта, который реализует интерфейсы для доступа к шифру. Таблица, ускоряющая операции, общая для всех экземпляров, эта таблица создаётся при инициализации модуля, а потом используется процедурами, реализующими шифр, только для чтения. Строго говоря, таблиц там несколько: есть “обратная” таблица для расшифрования и несколько вспомогательных (см. исходный код). Так как значения в таблицах не зависят от конкретного ключа, то их использование не требует повторного вычисления значений. В принципе, можно было бы просто записать эти таблицы в код в виде статического массива, так часто делают, но можно и генерировать программно, что делает код менее громоздким, а на производительность никак не влияет.
Базовые процедуры шифра (зашифрование/расшифрование) используют набор ключей раундов, которые создаются при инициализации конкретного экземпляра cipher – раундовые ключи как раз зависят от ключа шифрования, поэтому записываются в конкретный экземпляр структуры, в которой используются при повторных вызовах. То есть, разворачивание ключа тут тоже производится только один раз, при создании экземпляра cipher.
Что касается совместимости. И amd64, и arm64 – достаточно широкие понятия. В этой реализации шифра “Кузнечик” используются регистры и команды для разрядности 128-бит, которая соответствует разрядности шифра – в этом, собственно, смысл. Уже использование команд “длинной” арифметики из SSE4.1 для amd64 означает, что некоторые старые процессоры, полностью попадающие в amd64, не смогут, тем не менее, исполнять скомпилированный код, поскольку не поддерживают нужных команд. То же самое относится и к arm64 (например, выше я уже упомянул, что не использую “трёхрегистровую” команду XOR по этой же причине). Данная библиотека поддержку команд не проверяет, так что, вообще говоря, это нужно отслеживать отдельно, используя механизмы детектирования свойств аппаратуры, и, соответственно, либо заменять вызываемые функции, либо выводить предупреждение пользователю.
Комментарии (9) »
Представьте, что исследование карамельки выявило наличие внутри начинки. Для решения задачи размещения начинки внутри карамелек требуются некоторые особенные машины, которые нужны для формирования и обработки трубочек из карамельного состава. Опрошенные кулинары не знают, как такие машины изготавливают. Приготовить исходный раствор для корпусов карамелек, равно как и начинку из повидла, можно в кастрюле, это просто – типичная кухонная задача. Но вот создание машины, вкладывающей одно в другое, внезапно, требует станков для изготовления необычных металлических деталек, а это совсем другая история.
“Обратная разработка” (ревёрс-инжиниринг) нередко оказывается гораздо сложнее “прямой”. В частности, потому, что, при обратном движении, мыслимый процесс, приводящий к созданию целевого объекта, легко расщепляется, да ещё и в нескольких неожиданных плоскостях: мало того, что машина для помещения начинки в карамельки оказывается более замысловатой, чем кастрюля и сама карамелька с начинкой, так ещё и устроить эту машину можно многими способами, а измеримые свойства исходных карамелек вовсе не позволяют найти среди этих способов оптимальный, так как в карамельке сложность машины необратимым образом сворачивается в элементарный факт обнаружения начинки внутри корпуса.
(Кстати, близкий пример из области полупроводниковых схем – изменение проводимости, невидимое при “исследовании топологии”. Но, конечно, относится не только к микроэлектронике.)
Комментарии (2) »
Занимательное программирование. Почему вместо первого фрагмента кода (см. ниже) предлагается писать второй фрагмент (см. ещё ниже)?
Первый фрагмент:
if(matched != 0) // passwords match else //passwords don't match
Второй фрагмент:
if(matched == 0x69d61fc8) // passwords match else //passwords don't match
Потому что так предлагается защищаться от дефектов аппаратуры, приводящих к переключению битов в результате rowhammer-атак. Примеры взяты из исходной работы, где соответствующие “гаджеты” (то есть, способы сравнения в if) для проверки реквизитов доступа обнаруживаются в OpenSSH, sudo и др. Логика, стоящая за словом 0x69d61fc8, такая: rowhammer позволяет при помощи интенсивных операций с ячейками памяти переключать биты в соседних (физически) ячейках; поэтому, если используется условие “не равно нулю”, то достаточно перещёлкнуть один произвольный бит в нулевом значении переменной, чтобы равенство нарушилось и “пароли совпали” (passwords match); условие же “равно 0x69d61fc8” (или ещё какому-нибудь “перемешанному” значению) перещёлкиванием битов подогнать “несколько сложнее”.
Вообще, с точки зрения “просто программирования”, вариант matched != 0 – верный, работает, его можно использовать, никакой проблемы тут нет. А вот если подойти с точки зрения информатики и потребовать учитывать дефекты аппаратуры, то != 0 вдруг оказывается подвержено проблемам “аппаратурной” оптимизации ничуть не меньше, чем реализации алгоритмов, использующие строго фиксированное количество операций (вне зависимости от значений битов входных данных – constant-time, но не перепутайте с соответствующим классом сложности). Впрочем, в отличие от атак, измеряющих сдвиги времени исполнения, управляемое перещёлкивание произвольных битов – это, конечно, никакая не физическая особенность, а именно аппаратный дефект. Исправлять его, как и многие похожие, предлагается программно, а исправление приводит к не самым очевидным эффектам в коде на языке высокого уровня (ЯВУ) – потому что выбор значений констант для флагов таким образом, чтобы они оказывались далеко друг от друга в смысле rowhammer-метрики, это не очень близко к логике ЯВУ, да и нужно учитывать, что оптимизирующий компилятор может всё переписать на те же нули, но уже в машинном коде.
(Описание на OpenNET.)
Комментировать »
На сервисе audit.statdom.ru, в части тестирования настроек MX, добавился вывод сведений о совпадающих IP-адресах для разных имён MX-ов и чуть более развёрнутые сведения о TLS-сертификате почтового сервера (имя CN удостоверяющего центра).
Что касается разных имён для одинаковых IP-адресов, то тут речь вот о чём: предположим, что в зоне указано три имени MX – mx1.test.ru, mx2.test.ru, mx3.test.ru, но двум из этих имён соответствует общий адрес, например, mx1.test.ru имеет адреса 10.11.22.33, 10.22.33.44, а mx2.test.ru – 10.11.22.33, 10.55.55.55 (совпали 10.11.22.33). Это не то чтобы типовой случай, но регулярно встречается. Например, такое есть у Gmail. Причины тут могут быть разные, редко – это просто ошибка, а чаще – проекция представления администраторов и DevOps о методах балансировки (случай крупных сервисов) и защиты от нежелательных сообщений.
Кстати, с точки зрения сканера, конфигурация с повторяющимися IP создаёт некоторые занимательные затруднения для анализа TLS в случае STARTTLS. Дело в том, что ответы почтовых серверов кешируются, чтобы не надоедать “почтовикам” повторными запросами. Повторные запросы без попыток доставки сообщений – это, в мире администрирования почтовых серверов, самое страшное, что только может быть, так что робот к роботу тут должен подходить только с должным пиететом, расшаркиваясь и выжидая положенное время (посмотрите, кстати, на один из самых занимательных статистических отчётов по почтовым серверам Рунета – время ожидания ответа). Кешировать имеет смысл только по IP-адресу, потому что именно по IP-адресу устанавливается соединение. Так что, когда одинаковый адрес используется с разными именами, приходить повторно сканер не будет, но, вообще говоря, это означает, что сканер не сможет и получить сертификаты для разных имён, которые сервер, – гипотетически, – мог бы передать в ответ на не менее разные значения SNI (имя хоста в TLS).
Комментировать »
“Интернет через спутник” в обычном смартфоне – тема популярная. Сама возможность реализации широких каналов (до спутника) с типовым радиомодулем смартфона выглядит сомнительно: неустранимая задержка сигнала, проблемы синхронизации, “уползание” частот, новые виды помех – всё это добавляет трудностей. Понятно, впрочем, что если доработать наземные аппараты, то схема становится вполне реализуемой, как только на стороне провайдера имеются сотни низкоорбитальных спутников связи, объединённых в систему с единым управлением. Например, как в Starlink. Однако у подобной системы есть ряд занимательных возможностей, которые работают и без обеспечения широкополосного канала связи неким “абонентам со смартфонами”.
Понятно, что гипотетическая система “спутник и обычный смартфон”, уже по своему определению, должна достаточно точно знать местоположение абонентов, различая их по передатчикам аппаратов-смартфонов. Но, предположим, наземный аппарат всё же не смог успешно присоединиться к “космической сети” штатным способом и широкополосный канал установить не удалось. Однако это совсем не отменяет возможности для спутников принимать сигнал аппарата и классифицировать его. Пусть даже этот аппарат работает в настоящий момент с привычной наземной станцией: сигналы смартфонов содержат уникальные метки, которые ещё и установлены на разных логических уровнях, это позволяет их различать в пассивном режиме. Особенно хорошо получится различать отдельные аппараты по сигналам, если спутников много, они на низкой орбите, а также имеют общее синхронное время. Да, Starlink подходит. Понятно, что отсутствие заявленной возможности связи “смартфон-спутники” не означает, что сами приёмники на спутниках уже не используются для геолокации конкретных работающих смартфонов. Не обязательно вводить услугу связи “для смартфонов” – следить за смартфонами можно и так.
Возможности пассивного анализа сигналов мобильных сетей резко увеличиваются, если к спутниковой сети приёмников добавить доступ к внутренним сигналам и оборудованию сетей связи. Можно предположить, что даже и обычный операторский доступ к мобильным сетям (GSM/SS7 и пр.), если его наложить на специализированное спутниковое прослушивание эфира, позволит, что называется, развернуться. Что уж говорить про доступ к контроллерам и коммутаторам (в том числе, недокументированные возможности). Тут не нужно забывать, что базовые станции тоже излучают сигналы, которые могут принимать специализированные спутники (за вычетом “атмосферных эффектов”, да).
Спутниковая сеть имеет возможности и по передаче специально сконструированных сигналов. Так что можно произвольный смартфон и “подёргать”, и, теоретически, передать обновление прошивки для радиомодуля. То же самое возможно в отношении базовых станций. Поэтому такая спутниковая группировка, при желании, может ставить сложные активные помехи, мешающие работе наземной сети.
В общем, синхронная, динамическая, гибкая и перенастраиваемая радиосистема, распределённая по сотням низкоорбитальных спутников, это очень мощная платформа, позволяющая решать задачи, ранее принципиально недоступные. Но, конечно, всё это работает лишь для тех, у кого есть доступ к управлению такой системой.
Комментировать »
DNSSEC предоставляет механизм удостоверения адресной информации в DNS, что требует подтверждения факта отсутствия ответа на запрос. Собственно, это особенность не столько DNSSEC, сколько любой подобной криптографической системы: необходима возможность криптографического подтверждения, что какие-то записи действительно отсутствуют, потому что иначе активный аткующий может просто удалить из DNS-ответов записи, обеспечивающие безопасное делегирование (DS-записи), тем самым сымитировав отсутствие DNSSEC в атакуемой зоне. Отсутствию записей и имён в терминах DNS соответствует NXDOMAIN (нет имени) и NODATA (нет данных записи запрошенного типа). NXDOMAIN – это когда спросили имя test.example.com, а его в зоне example.com нет. NODATA – спросили TXT-запись для test.ru, имя test.ru есть, но ему не соответствует TXT-записи (NODATA это ANSWER:0 со статусом NOERROR, если в терминах BIND).
Так вот, DNSSEC работает с электронными подписями, однако подписать пустое множество здесь нельзя (хотя бы потому, что все пустые множества одинаковые, а это означает, что подобную подпись можно будет скопировать и применять в контексте отсутствия любой записи). Так что типовая реализация DNSSEC как бы подписывает конкретные “дыры”, но для этого заключает их в некоторую внешнюю конструкцию.
Например, для NXDOMAIN “подписываются” два ближайших имени, между которыми оказалось бы запрашиваемое, если все имена в зоне упорядочить. Известно, что эта реализация позволяет третьей стороне проиндексировать имена, существующие в зоне, поскольку ответ на запрос со специально составленным несуществующим именем позволяет видеть соседние существующие имена – это называется DNSSEC zone walking (индексация зоны), и предоставляет хрестоматийный материал для обучения студентов направления “Информационная безопасность”, но не более. Строго говоря, есть два основных варианта решения проблемы “подписывания дыр”: они называются NSEC и NSEC3, а последний предоставляет больше защиты от индексации зоны, однако сейчас это всё лишь иллюстративные детали, поскольку вообще странно считать, будто заведомо публичные записи в зоне должны быть защищены таким странным способом от “обнаружения” – см., впрочем, про “принцип Керкгоффса“. Предложены способы, предотвращающие индексацию зоны путём “подставных” ответов, содержащих специально сгенерированные имена.
Для NODATA подписывается перечень существующих записей для запрошенного имени, а логика близка к NXDOMAIN, но на практике реализовать можно более удобным способом (см. ниже). Сопроводительная информация о границах “дыр” передаётся в записях NSEC (или NSEC3, но в этой заметке рассматривается только NSEC – дальше будет понятно, почему).
При полностью динамической работе DNS на стороне авторитативного сервера всякое противодействие индексации зоны требует динамических подписей. Однако в таком случае обработка NXDOMAIN быстро превращается в проблему, поскольку требуются дополнительные вычисления. Особенно, если в зоне много имён. Грубо говоря, если у вас есть миллион записей с именами хостов в некоторой базе данных, то уже для обнаружения соседних имён придётся строить какой-то индекс и так далее. Если же состав записей постоянно изменяется, а ответ зависит и от видимых характеристик клиента, и от текущего состояния большей системы на стороне сервера, то вычисление NXDOMAIN может вылететь в копеечку (в том числе, из-за большого размера ответа с подписями). Оригинальное решение тут первыми применили в Cloudflare, ещё в 2016 году. Решение основано на модификации логики обслуживания DNSSEC – предлагается, фактически, исключить понятие отсутствующего имени или отсутствующей записи, что позволит генерировать виртуальные подписи на лету (но, обратите внимание, это не отменяет кэширования сгенерированных ответов).
Иными словами – сервер выдаёт “нечестные” ответы. Например, сервер не отвечает резолверу со статусом NXDOMAIN вообще, а вместо этого использует вариант NODATA: на запрос о реально не существующем в зоне имени сервер отвечает, что есть имя, состоящее из префикса \000., приписываемого к запрошенному имени, то есть, это как бы граница (которой в реальной зоне нет), а ответ поэтому содержит одну NSEC-запись; подпись же вычисляется на лету. Для случая “настоящего” NODATA – всё работает аналогично: сервер отвечает, что есть все мыслимые ресурсные записи, кроме той, которую запросил резолвер. Например, если резолвер спрашивает A-запись, то авторитативный ответ будет содержать указание в NSEC на существование NS, SOA, TXT, LOC, SRV и прочих DNS-записей, но не A-записи. Это тоже позволяет генерировать ответы динамически. То есть, авторитативный сервер “обманывает” резолвер, но делает это для того, чтобы экономить вычислительные ресурсы (поэтому механизм называется black lie – вместо white lie, однако это детали литературной терминологии).
Всё это несложно сейчас увидеть снаружи на примере Cloudflare: попробуйте взять dig и проверить самостоятельно на той или иной зоне, поддерживающей DNSSEC на авторитативных серверах Cloudflare. Такая реализация не только позволяет работать с динамической DNS, но и экономит место в ответах (другой существенный способ экономии – ECDSA, которую только и использует Cloudflare, так как ECDSA-подписи короче RSA).
Насколько такой метод универсален и эффективен? Для больших, постоянно изменяющихся наборов DNS-данных, эффективен, однако требует программного решения собственной разработки. При этом, если DNS-зона не особенно часто изменяется, то смысла в динамическом вычислении подписей меньше. Для многих зон обычным является изменение содержания DNS-записей, скажем, раз в месяц (или даже раз в год). С другой стороны, динамическая “подмена” NXDOMAIN может защитить от индексации зоны.
Вычисление подписи на лету означает, что секретный ключ и реализация алгоритма подписи применяются гораздо чаще, чем при “статическом” варианте. Однако это вопрос модели угроз. Так, для атак, использующих побочные каналы утечки, требуется, чтобы можно было вызывать реализацию криптосистемы много раз (естественно, за разумный промежуток времени). Существенным подспорьем тут будет возможность получать подписи на данных известного атакующему состава и в выбранном формате – это, отчасти, как раз и позволяет реализация подмены NXDOMAIN на NODATA. Впрочем, каких-то огромных практических рисков на этом направлении не просматривается.
Комментировать »
Кстати, если посмотреть в DNS-зону cloudflare.com (это, понятно, Cloudflare) на предмет политики DMARC, то там в поле rua указан почтовый адрес в cyber.dhs.gov:
$ dig -t TXT _dmarc.cloudflare.com +short "v=DMARC1; p=reject; pct=100; rua=[...]mailto:reports@dmarc.cyber.dhs.gov"
Что это означает? Имя cyber.dhs.gov – это в Штатах агентство CISA (Cybersecurity and Infrastructure Security Agency), относящееся к DHS, то есть, к министерству внутренней безопасности (Department of Homeland Security). Опция rua в строке DMARC обозначает адрес (адреса), по которому принимающие почтовые серверы могут отправлять отчёты об обработке почтовых сообщений (допускаются разные протоколы для доставки, но обязательный – mailto, электронная почта). Так что, запись означает, что сборщик отчётов DMARC CISA может сразу принимать сведения о получении почты с адресами в домене cloudflare.com. Это не обязательно сообщения, действительно отправленные почтовой системой Cloudflare – может быть и нежелательная рассылка с “подспуфленным” адресом: для обнаружения таких писем как раз нужны DMARC и DKIM.
Комментировать »
Новый