Перейти к содержимому

Модель угроз Tessera

Документ адресован безопаснику, который оценивает tessera перед развёртыванием, и отвечает на четыре вопроса: что защищено, от чего, чем это доказано и что защите не поддаётся.

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

Как читать документ:

  • §1–§2 — границы оценки: целевой объект (TOE) и допущения о развёртывании, при которых справедливы выводы ниже.
  • §3 — угрозы, от которых модуль защищает: механизм защиты и evidence на каждую.
  • §4 — угрозы, от которых модуль НЕ защищает, и чем их закрывать.
  • §5–§6 — поверхность атаки, модель привилегий процессов, lockout-устойчивость и модель нарушителя (Н1–Н4).
  • §7–§8 — атак-tree для ключевой угрозы (3.5) и список тестов, доказывающих защиты.
  • §9 — специфика МКЦ Astra в strict-mode.
  • §10 — сводный ранжированный реестр угроз (T1–T12) по итогам систематического прохода по коду.

Под tessera понимается:

  • PAM-модуль libpam_tessera.so (cdylib);
  • демон tessera (бинарь);
  • крейты ядра tessera_core и протокола tessera_proto;
  • поставочные конфигурационные файлы dist/config/*.example;
  • systemd-юнит и tmpfiles-сниппет;
  • скрипт интеграции dist/scripts/integrate-pam.sh.

В TOE не входят:

  • ядро Astra Linux SE;
  • libpam, libssl3, libudev, libdbus, libsystemd;
  • gost-engine (отдельное СКЗИ ФСБ);
  • PKCS#11-модули вендоров токенов (отдельные СКЗИ ФСБ);
  • пользовательские хуки, прописанные в [[hooks]];
  • Inkscape, fly-dm, gdm и прочие потребители PAM.
#ДопущениеПочему важно
2.1Машина физически защищена (МКЦ Astra или эквивалент).Без МКЦ root-компрометация → бесполезность модуля.
2.2Целостность системы на момент установки контролируется (МКЦ + verified boot, если есть).Подмена gost-engine.so или libpam_tessera.so обходит модуль.
2.3CA-инфраструктура работает корректно: ключи в HSM, выпуск контролируется регламентом.Компрометация CA private key → катастрофическая компрометация контура.
2.4PIN-коды пользователей не разглашаются, не записываются на бумаге у компьютера.PIN — единственная защита токена при физическом доступе.
2.5Сертификаты выпускаются УЦ с обязательными расширениями pam_cert_host_binding и pam_cert_allowed_roles.Без расширений сертификат не авторизует ни одной роли ни на одном устройстве — fail-closed.
2.6Администратор имеет «backup-tty» во время правки PAM-стека.Защита от lockout при ошибочной конфигурации.
2.7Резервный пользователь с парольной аутентификацией не удалён.Lockout-prevention при сбое в tessera.

Каждая угроза описана по схеме: описание → STRIDE-категория → mitigation → evidence (код, конфиг, тест).

  • Описание: атакующий пытается перебрать пароль локального пользователя.
  • STRIDE: Spoofing.
  • Mitigation: парольной аутентификации tessera не реализует. Любая попытка ввода пароля проваливается на этапе pam_conv.
  • Evidence: в crates/pam_tessera/src/entry.rs отсутствует вызов pam_authtok_get. Аутентификация идёт через challenge-response с приватным ключом (crates/tessera_core/src/challenge/).

3.2 Утечка пароля при подсматривании / фишинге

Заголовок раздела «3.2 Утечка пароля при подсматривании / фишинге»
  • Описание: plain text password leakage.
  • STRIDE: Information Disclosure.
  • Mitigation: пароля нет (см. 3.1). PIN токена не передаётся в PAM-стек и в журналы.
  • Evidence: crates/tessera_core/src/secret.rs — обёртка Secret<T: Zeroize> зануляет PIN при Drop. PIN никогда не передаётся как форматный аргумент tracing-макроса (см. также crates/tessera_core/src/pkcs12/).
  • Описание: атакующий копирует .p12 с чужого USB и пытается использовать его на своей машине.
  • STRIDE: Spoofing.
  • Mitigation (Mode A): .p12 зашифрован парольной фразой; без фразы дешифровка невозможна. Mode A считается режимом «средней» защиты — для production применяется Mode B.
  • Mitigation (Mode B): ключ non-extractable. Тест проверяет атрибуты CKA_EXTRACTABLE = false.
  • Evidence: тест crates/tessera_core/tests/pkcs11_hardware_negative.rs

3.4 Использование чужого токена без знания PIN

Заголовок раздела «3.4 Использование чужого токена без знания PIN»
  • Описание: атакующий получил токен (украл/подобрал на стуле), но PIN не знает.
  • STRIDE: Spoofing.
  • Mitigation: PIN-prompt через PAM conversation; после N неудачных попыток (pkcs11_max_pin_attempts, default 3) модуль отказывает. После N попыток на самом токене — он блокируется на уровне аппаратного счётчика.
  • Evidence: тест crates/tessera_core/tests/pin_loop.rs проверяет лимит попыток.

3.5 Использование валидного токена на чужой машине

Заголовок раздела «3.5 Использование валидного токена на чужой машине»
  • Описание: атакующий легально владеет токеном (или украл его), но пытается использовать на машине, где этот токен не разрешён.
  • STRIDE: Spoofing + Elevation of Privilege.
  • Mitigation: проверка X.509-расширения pam_cert_host_binding — записи в нём (* / sha256:<HEX> / raw machine_id) сравниваются с host_id_hash = sha256(host_id) запрашивающей машины. Если ни одна запись не совпала — PAM_AUTH_ERR (HostNotAllowed). Само расширение защищено подписью сертификата CA — изменить его без компрометации CA нельзя.
  • Evidence:

3.6 Использование сессии после ухода пользователя

Заголовок раздела «3.6 Использование сессии после ухода пользователя»
  • Описание: пользователь отошёл от рабочего места, не извлекая токен; либо извлёк, но забыл закрыть сессию.
  • STRIDE: Tampering + Elevation of Privilege.
  • Mitigation: мониторинг udev REMOVE-событий через monitord; по подтверждённому removal (с учётом usb_removed_grace_seconds) — LockSession или TerminateSession через D-Bus к logind.
  • Evidence:
  • Описание: атакующий с file-write правами заменяет pam_tessera.so или tessera подделанным бинарём.
  • STRIDE: Tampering.
  • Mitigation:
    • На Astra Linux SE production-режим — astra-digsig-control в enforce. ELF-файлы из пакета tessera должны быть подписаны через сборочный CI Astra-партнёра (bsign GPG-ключом из доверенной связки /etc/digsig/keys/); подмена бинаря без соответствующей подписи отвергается ядром на execve(2) / mmap(2).
    • Если ЗПС переведён в logging-only, защита снижается до dpkg --verify и прав 0755 root:root на бинарь — этот режим допустим только на dev-машинах. Production-deploy без подписи запрещён регламентом эксплуатации.
  • Evidence: см. install.md §1.5 «Preflight: USBGuard и Astra ЗПС (DIGSIG)» — там описан и режим проверки, и команды диагностики.

3.7 Утечка приватного ключа из памяти процесса

Заголовок раздела «3.7 Утечка приватного ключа из памяти процесса»
  • Описание: атакующий читает память процесса (через ptrace, /proc/pid/mem, минидамп) и пытается извлечь PIN или приватный ключ.
  • STRIDE: Information Disclosure.
  • Mitigation:
    • PIN хранится в Secret<T: Zeroize> — зануляется при Drop.
    • Приватный ключ в Mode B никогда не покидает токен (PKCS#11 non-extractable).
    • В Mode A парольная фраза дешифровки используется единожды и обнуляется после flow::authenticate.
    • systemd unit ставит NoNewPrivileges=yes, ProtectKernelTunables=yes, RestrictNamespaces=yes — затрудняет ptrace со стороны.
  • Evidence:

3.8 Сертификат без расширений или с подделанным расширением

Заголовок раздела «3.8 Сертификат без расширений или с подделанным расширением»
  • Описание: атакующий пытается использовать сертификат, в котором расширений pam_cert_host_binding / pam_cert_allowed_roles нет совсем, либо пробует встраивать «подделанные» записи в обход УЦ.
  • STRIDE: Tampering + Spoofing.
  • Mitigation:
    • Mandatory-extension policy: отсутствие любого из расширений в leaf-сертификате — это безусловный отказ: нет host-расширения → HostExtensionMissingPAM_AUTH_ERR; нет allowed_roles → удостоверение не даёт ни одной роли → PAM_PERM_DENIED. Никаких «мягких» fallback’ов нет, и допуска на стороне устройства не существует: рамки назначает выпускающий.
    • Защита подписью CA: содержимое расширения покрыто подписью сертификата. Изменить запись без приватного ключа CA невозможно; подделать сертификат полностью — задача компрометации УЦ (см. 4.8).
    • Проверка цепочки: при выпуске сертификата нештатным «доверенным» CA срабатывает [trust].anchors + опционально [trust.pinning].
    • Повреждённое DER-кодирование (мусор в extnValue) → отказ fail-closed: для host-расширения *ExtensionMalformedPAM_AUTH_ERR, для allowed_roles список считается пустым (а не игнорируется) → запрошенная роль не покрыта → отказ.
  • Evidence:
    • реализация — парсеры tessera_core::x509::{host_binding_ext, allowed_roles_ext} + verify_cert_scope в модуле tessera_core::host_binding;
    • таблица семантики — docs/cert-issuance.md.
  • Описание: атакующий с file-write правами заменяет config.toml на конфигурацию с ослабленным [trust] или отключённой revocation.
  • STRIDE: Tampering.
  • Mitigation:
    • config.toml не подписывается, но защищён правами 0640 root:root (см. debian/postinst);
    • dpkg --verify tessera обнаруживает изменение поставочных файлов (но не пользовательских правок config.toml после установки);
    • изменение конфига требует root-доступа — это уже вне модели угроз PAM-уровня.
  • Evidence: debian/postinst + dist/tmpfiles/tessera.conf.
  • Описание: атакующий пытается подключиться к /run/tessera/monitord.sock и подменять ответы.
  • STRIDE: Tampering + Spoofing.
  • Mitigation:
    • сокет в /run/tessera/ имеет права 0660 tessera:tessera (создаётся демоном под User=tessera; каталог — 0750 tessera tessera из tmpfiles.d);
    • monitord проверяет peer’а через SO_PEERCREDuid != 0Error { code: 1003 (UNAUTHORIZED) } + разрыв.
  • Evidence:
  • Описание: атакующий, перехвативший challenge и подпись, пытается предъявить их повторно на новой попытке аутентификации.
  • STRIDE: Spoofing.
  • Mitigation: challenge генерируется свежий на каждую попытку через getrandom (16 байт) внутри cdylib (см. entry.rs::fresh_session_id и challenge/).
  • Evidence:
  • Описание: атакующий подсовывает специальные символы в pam_user (или другой placeholder) с целью вызвать команду в хуке с подделанными аргументами.
  • STRIDE: Elevation of Privilege.
  • Mitigation: placeholder’ы (${pam_user}, ${cert_cn}, …) подставляются как отдельные argv-элементы, не через интерполяцию в shell. Реализация — fork+execve, без system(3).
  • Evidence:
#УгрозаРекомендуемый компенсирующий контроль
4.1Rootkit / компрометация ядраМКЦ Astra, IMA, EDR.
4.2Физический доступ к разлоченной сессии до срабатывания grace.Уменьшить usb_removed_grace_seconds; админ-политика.
4.3Извлечение ключа из токена при компрометации PIN + физический доступ к токену + спецаппарат.Аппаратные средства токена (anti-tamper в чипе).
4.4Mode A с .p12 без пароля или со слабым паролем.Не использовать Mode A на production. Применять Mode B.
4.5Уязвимости в gost-engine.Своевременные обновления Astra; СКЗИ ФСБ ответственно за патчи.
4.6Side-channel атаки на токен (electromagnetic, power, timing).Аппаратные anti-tamper меры.
4.7Социальная инженерия (отдать токен и сообщить PIN).Обучение, политика.
4.8Компрометация УЦ (CA private key утёк).HSM для CA, разделение ролей; [trust.pinning] ограничивает зону поражения (blast radius) до уровня pinned-roots.
4.9Уязвимости в libpam, libssl3, ядре.Apt-обновления, CVE-мониторинг.

[host_identity].sources задаётся как упорядоченный fallback, а не как «принять, если совпало с любым из источников». resolve() берёт первый успешный источник и считает только его host_id_hash; cert обязан зашифровать именно это значение (через pam_cert_host_binding).

Это сделано намеренно. Multi-source matching «совпало хотя бы с одним» эквивалентен «weakest source wins» (побеждает слабейший источник): атакующий с root правами подменяет самый писабельный источник (например /sys/class/dmi/id/board_serial под qemu или custom_command через shim), и host-binding обходится. Поэтому соответствие проверяется ТОЛЬКО против resolved-источника per fallback policy.

Эффект для администраторов: после смены [host_identity].sources требуется перевыпуск cert’а (новый источник → новый host_id_hash). Drift между скриптом выпуска и развёрнутой конфигурацией ловится через journalctl -t tessera | grep 'host_id resolved' (см. troubleshooting.md).

#ПоверхностьЗащита
5.1PAM-стек (/etc/pam.d/*)Стандартная безопасность PAM; интегрируется через @include tessera.
5.2libssl3 / libcryptoСистемные обновления через apt.
5.3PKCS#11-модуль (Рутокен / JaCarta)СКЗИ ФСБ; closed-source; доверяем при наличии действующего сертификата.
5.4udev-событияНе аутентифицированы, но мы внутри kernel-namespace и доверяем udev.
5.5IPC-сокет /run/tessera/monitord.sockSO_PEERCRED uid=0 + права 0660. Если root уже компрометирован — модуль уже бесполезен.
5.6Хуки в [[hooks]]Whitelist placeholder’ов, fork+execve, таймауты. Сам хук — ответственность администратора.
5.7Конфигурационные файлы /etc/tessera/config.toml и trust-anchorsПрава 0640 root:root. Ручное управление.
5.8Плагины бэкендов /usr/lib/tessera/plugins/*.soEd25519-подпись по вшитому на сборке списку ключей до dlopen (в release неотключаемо); RTLD_NOW+RTLD_LOCAL; строгая ABI-валидация; любой отказ → StubBackend + аудит. Подробно — §5.6.
ПроцессКонтекст / UIDHardeningИзвестный остаточный риск
pam_tessera.soUID PAM-вызывателя (sudo/login/fly-dm — обычно root на этапе auth); архитектурное требование PAM.#![forbid(unsafe_code)] на tessera_proto; panic_guard на каждой C-границе → PAM_AUTHINFO_UNAVAIL; Secret<T: Zeroize> для PIN.Загрузка в адресное пространство rooted-процесса — компрометация хоста компрометирует и модуль (вне TOE, см. 4.1).
tesseraUser=tessera / Group=tessera — выделенная системная учётная запись без shell, создаётся debian/postinst.ProtectSystem=strict + ReadWritePaths=…, ProtectHome=yes, PrivateTmp=yes, NoNewPrivileges=yes, ProtectKernelTunables/Modules/ControlGroups=yes, RestrictNamespaces=yes, RestrictRealtime=yes, LockPersonality=yes, CapabilityBoundingSet=CAP_DAC_READ_SEARCH, AmbientCapabilities=CAP_DAC_READ_SEARCH. Привилегированные D-Bus вызовы к logind гейтятся polkit-правилом.MemoryDenyWriteExecute=no (оставлен off из-за W^X-релаксации в OpenSSL/gost-engine); полная W^X-сэндбоксизация — задача после benchmarking-стадии (см. systemd-юнит и backlog к 0.1.2).

pam_tessera.so исполняется в контексте PAM-вызывателя — это архитектурное ограничение PAM-стека, не выбор реализации; снизить привилегии cdylib без перепроектирования PAM-протокола нельзя. tessera начиная с 0.1.1 уже вынесен в отдельную системную учётную запись — root-привилегии для D-Bus-действий на logind выдаются точечно через polkit-правило, поставляемое пакетом.

PAM-стек, в который интегрирован tessera, превращает USB-токен в жёсткий второй (или единственный, см. cert-only) фактор. Это сознательный security-выбор; цена выбора — устойчивость к потере токена ложится на эксплуатацию, а не на сам модуль:

РежимПотеря токенаUSBGuard блокирует токенAstra ЗПС в enforce без подписи бинаря
2faМожно войти по паролю.То же — пароль работает.PAM-модуль не загрузится → fallback на пароль (auth required сорвёт логин).
optionalМожно войти по паролю.То же.То же.
cert-onlyLockout. Локальный root тоже не зайдёт.Lockout.Lockoutauth [success=done default=die].

Компенсирующие контроли для cert-only (обязательные перед deploy’ом):

  • резервный канал доступа без tessera (см. install.md §9) — отдельный sshd-stack UsePAM=no или sudoers-правило для аварийной учётной записи;
  • запасной токен с тем же pam_cert_allowed_roles для каждого привилегированного пользователя;
  • задокументированная процедура rescue-recovery (см. install.md §11 «Замок-аут после неудачной правки PAM»).

Платформенные enforcement-бэкенды (ParsecBackend для Astra МКЦ; SELinux — на будущее) поставляются не compile-time feature’ами, а подписанными runtime-плагинами: один открытый бинарь загружает бэкенд из отдельного .so. Это убирает комбинаторику сборок, но вводит новую поверхность — исполнение стороннего кода в адресном пространстве rooted-процесса (pam_tessera на этапе auth, см. 5.1). Ниже — чем эта поверхность закрыта.

Код: crates/tessera_core/src/plugin/ (verify.rs — подпись, loader.rs — загрузка и мост C-ABI ↔ MacBackend, header.rs — конверт/vtable, audit.rs — события).

  • Алгоритм (по факту имплементации): Ed25519 detached-подпись над точными байтами .so. Файл подписи — <plugin>.so.sig, формат ed25519:<128 hex>. Префикс алгоритма обязателен — старую подпись нельзя переинтерпретировать, когда будущая ABI добавит ГОСТ.
  • Якорь доверия: список публичных ключей вшивается на сборке (TESSERA_PLUGIN_PUBKEYS — 32-байтные raw-ключи Ed25519 в hex). Пустой список → NoKeys: любой плагин отвергается (fail-closed).
  • Порядок (loader.rs): is_fileверификация подписи → чтение и SHA-256 (в аудит) → dlopen. Подпись проверяется до любого кода плагина; подпись чужим ключом или изменённый образ дают Invalid. Оговорка: подпись, SHA-256 и dlopenтри отдельных чтения файла по пути, поэтому «изменённый образ не грузится» строго держит лишь без конкурентной подмены между этими чтениями (TOCTOU, см. 5.6.5).
  • Верификация неотключаема в поставке. Она пропускается только под cfg(debug_assertions); .deb собирается release-профилем (без debug-assertions), а debug-сборка в пакет не попадает. Нетипичный release с включёнными debug-assertions проверку обошёл бы — страхует smoke-тест пакета на отклонение неподписанного плагина.
  • dlopen с RTLD_NOW | RTLD_LOCAL: неразрешённые символы отвергаются сразу (нет lazy-binding), символы плагина не текут в глобальный namespace процесса.
  • Валидация ABI-контракта конверта: abi_version == 1, kind == enforcement, non-null name/version/vtable, имя из заголовка совпадает с явно выбранным конфигом. Несовпадение полей и null-указатели → отказ. Но заголовок разбирается по указателям (&*header_ptr, CStr::from_ptr, каст vtable), поэтому подписанный, но битый/версионно-разошедшийся заголовок с non-null, но невалидным указателем — UB на этапе парсинга, до panic-границы (остаточный риск, см. 5.6.5). Планка здесь — доверие к вендорской подписи.
  • Panic-граница (контракт плагина): каждый callback обязан поймать panic и вернуть PLUGIN_PANIC до пересечения C-ABI (unwind не может безопасно пересекать независимо слинкованные dylib-рантаймы); хост это не форсит, а полагается на контракт. PLUGIN_PANIC → аудит и деградация, а не крэш процесса. Исключение — teardown: без статус-кода, его паника не логируется.
  • Принятый плагин остаётся загружен на весь процесс (ManuallyDrop) — для активного бэкенда dlclose не вызывается, висячих указателей нет. На путях отказа после dlopen (entry/header/abi/kind/name/init) библиотека выгружается штатным dlclose; при провале init teardown не зовётся.
  • Активный бэкенд называется явно конфигом ([mac] backend = "parsec"); автоактивации нет. Без имени — StubBackend.
  • Любой отказ (нет файла, подпись, dlopen, конверт, ABI, init) → plugin_rejectedдеградация в StubBackend. Fail-closed для ролей, требующих МКЦ, обеспечивает не загрузчик, а политика роли (change role-format): StubBackend не выдаёт МКЦ-метку, и роль с требованием целостности отклоняется.
СобытиеКогдаПоля
plugin_loadedплагин принятname, plugin_version, kind, sha256
plugin_rejectedлюбой отказ загрузкиpath, reason (missing/signature/dlopen/header/abi/kind/init)
plugin_inactive_file.so лежит в каталоге, но не выбранpath
plugin_paniccallback вернул PLUGIN_PANICname, entry_point

plugin_loaded эмитится сразу после initдо финального probe, поэтому означает «инициализированный кандидат», а не «бэкенд стал активным» (runtime-режим ещё может деградировать в StubBackend). SHA-256 в событии снят отдельным чтением файла, не с образа в памяти (см. TOCTOU в 5.6.5), — это форензик-указатель на кандидата, а не крипто-привязка к активному образу.

РискОценка
Подмена .so локальным rootКаталог root:root; запись = root уже скомпрометирован (вне TOE, 4.1). Подпись — defense-in-depth: даже root не подсунет свой бэкенд без приватного ключа вендора, не пересобрав бинарь (а бинарь — под dpkg-/ЗПС-подписью, см. 3.6.1).
Сборка без вшитых ключейОшибка релиза → NoKeys → enforcement пропадает, но fail-closed + plugin_rejected. Закрывается smoke-проверкой пакета.
debug-сборка без верификацииВне модели поставки (cfg(debug_assertions), не в .deb).
Подпись Ed25519, а не ГОСТПодпись плагина — вендорская инфраструктура доставки, а не сертификатная цепочка; Ed25519 приемлем. Обязательный префикс формата оставляет миграцию на ГОСТ без двусмысленности.
Вредоносный/багованный плагин после загрузкиИсполняется с привилегиями процесса. Граница — подпись (вендор доверенный) и panic-guard (кооперативный, на стороне плагина); плагин по контракту thread-safe.
Битый/версионно-разошедшийся подписанный заголовокnon-null, но невалидный указатель в заголовке → UB при парсинге (&*header_ptr/CStr::from_ptr/vtable-каст) до panic-границы. Планка — доверие к вендорской подписи; типизация конверта ловит только несовпадение полей и null.
TOCTOU: подпись↔SHA-256↔dlopenТри отдельных чтения файла по пути; конкурентный писатель мог бы подсунуть иной образ между верификацией и dlopen. Требует записи в root:root-каталог = root уже скомпрометирован (вне TOE, 4.1); привязка к одному immutable-чтению (memfd) — возможная будущая мера.
УровеньОписаниеОжидание модуля
Н1Внешний нарушитель без физического доступа, без токена, без PIN.Ноль успехов.
Н2Внешний нарушитель добыл токен, но не знает PIN.Ноль (защита PIN-кодом + лимит попыток).
Н3Внутренний нарушитель: легитимный пользователь пытается использовать токен на запрещённой машине или для входа в чужую ролевую учётную запись.Ноль (host_binding + allowed_roles в расширениях, защищённых подписью CA).
Н4Внутренний нарушитель: администратор с root-доступом.Не моделируется — admin доверен по построению.

7. Атак-tree для угрозы 3.5 «использование валидного токена на чужой машине»

Заголовок раздела «7. Атак-tree для угрозы 3.5 «использование валидного токена на чужой машине»»
graph TD
R[Получить shell под alice<br/>на terminal-XYZ]
R --> A1[Украсть токен alice<br/>+ узнать PIN]
R --> A2[Подделать сертификат<br/>с CN=Alice]
R --> A3[Получить от УЦ сертификат<br/>с расширением, разрешающим terminal-XYZ]
R --> A4[Подменить host_id<br/>чтобы выдать себя за разрешённую машину]
A1 --> A1a[Социальная<br/>инженерия]
A1 --> A1b[Кража + наблюдение]
A1a -.->|компенс.| C1[Обучение пользователей]
A1b -.->|компенс.| C2[Политика «токен с собой»]
A2 --> A2a[Скомпрометировать CA]
A2 --> A2b[Найти коллизию хеша]
A2a -.->|митиг.| M1[CA в HSM<br/>+ trust.pinning]
A2b -.->|митиг.| M2[Whitelist стойких алгоритмов<br/>3.13]
A3 --> A3a[Сертификат без расширений]
A3 --> A3b[Скомпрометировать CA]
A3a -.->|митиг.| M3[Mandatory-extension policy<br/>3.8]
A3b -.->|митиг.| M1
A4 --> A4a[machine_id<br/>совпал случайно]
A4 --> A4b[Подменить /etc/machine-id]
A4 --> A4c[Подменить DMI board serial]
A4a -.->|митиг.| M4[host_id основан на нескольких источниках]
A4b -.->|митиг.| C3[Целостность файлов в МКЦ]
A4c -.->|митиг.| C4[Невозможно без вскрытия корпуса]

8. Список тестов, доказывающих заявленные защиты

Заголовок раздела «8. Список тестов, доказывающих заявленные защиты»
УгрозаТест(ы)Файл
3.3non-extractable checkcrates/tessera_core/tests/pkcs11_hardware_negative.rs
3.3PKCS#12 wrong passwordcrates/tessera_core/tests/pkcs12.rs
3.4PIN attempt limitcrates/tessera_core/tests/pin_loop.rs
3.5host_binding mismatch (host-запись не совпала)crates/tessera_core/src/host_binding.rs (host_mismatch_rejected)
3.5end-to-end auth с расширениями host/user bindingcrates/pam_tessera/tests/auth_e2e_p12.rs
3.6USB removal → grace → lockcrates/tessera_cli/tests/udev_simulation.rs
3.6suspend/resume игнорирует transient REMOVEcrates/tessera_cli/tests/suspend_grace.rs
3.7Secret zeroization в Dropюнит-тесты в crates/tessera_core/src/secret.rs
3.8сертификат без расширений → отказcrates/tessera_core/src/host_binding.rs (missing_*_extension_rejected) + crates/tessera_core/tests/cert_extensions_parse.rs
3.10uid≠0 peer отвергаетсяcrates/tessera_cli/tests/peercred.rs + ipc_auth.rs
3.11challenge не повторяетсяcrates/tessera_core/tests/challenge_dispatch.rs
3.12argv-injection невозможенcrates/tessera_core/tests/hook_security_integration.rs
3.13weak signature → DisallowedSignatureAlgorithmcrates/tessera_core/tests/chain_verify.rs
3.13ГОСТ chain verify (реальный engine)crates/tessera_core/tests/gost_chain_verify_real.rs
Reproducibility / supply-chainreproducible build (двойная сборка)scripts/verify-reproducible-build.sh
  • 9.1.1 Privilege-escalation via MAC label. Сертификат декларирует чрезмерно высокий MAX_INTEGRITY; без контроля рантайма пользователь поднимает уровень сессии выше потолка хоста.
  • 9.1.2 Bypass through missing extension. Сертификат, выпущенный до развёртывания МКЦ, не содержит MAX_INTEGRITY — без cert_integrity = "required" сессия открывается без метки и получает «прозрачный» доступ.
  • 9.1.3 DER-tampering. Атакующий, контролирующий УЦ, кладёт битый/нестандартный DER в расширение, рассчитывая на сбой парсера и fallback-поведение «accept-by-default».
  • 9.1.4 sessions.json TOCTOU. Файл состояния перезаписывается атомарно. Ранее irelax-лейбл на новом inode восстанавливался отдельной сисколлой → окно гонки, в котором демон с MAC=0 не может прочитать только что записанный файл. В 0.3.0 устранено: файл лежит на tmpfs /run/tessera/ (RuntimeDirectory=), родительский каталог получает iinh, лейбл накладывается на fd до публикации имени через pdp_set_fd (см. §9.2.4); reboot снимает состояние полностью.
  • 9.1.5 host_id rebind. Подменив host_id, атакующий перепривязывает сертификат к другому хосту.

9.1.6 irelax + UID 0 = подделка (в границах модели, вне охвата митигаций)

Заголовок раздела «9.1.6 irelax + UID 0 = подделка (в границах модели, вне охвата митигаций)»

monitord и pam_tessera.so накладывают метку irelax на собственные файлы (/run/tessera/monitord.sock, /run/tessera/sessions.json) через привилегию PARSEC_CAP_CHMAC пользователя tessera. Это необходимо: инженер с НКЦ=1 должен иметь возможность писать receipt’ы в демон на уровне lvl=0 через сокет, который и сам помечен irelax.

Враждебный процесс под UID 0 (root-эквивалент) с той же привилегией PARSEC_CAP_CHMAC может наложить такие же метки irelax на собственные файлы и подделать записи в sessions.json, либо подключиться к сокету с произвольного уровня НКЦ. В коде это не блокируется: защита здесь — граница по UID (root-эквивалентность аксиоматически побеждает под МКЦ), а не целостность.

Митигации, лежащие за границами этой угрозы:

  • DAC-права 0600 tessera:tessera на sessions.json и 0750 tessera:tessera на родительском каталоге ограничивают доступ на запись процессам без привилегии PARSEC_CAP_CHMAC.
  • Проверка digsig на бинарях tessera(-monitord) детектирует подмену во время исполнения.
  • Аудит-запись на каждое событие mac_apply_failed / mac_caps_missing выявляет неожиданное поведение бэкенда.

Граница доверия этого дизайна — UID 0 против non-root, а не уровень целостности. Угроза признана лежащей в границах модели и явно вынесенной за пределы митигаций для PAM-слоя: защита от подделки со стороны UID 0 — задача ОС (digsig, ЗПС, аппаратный root-of-trust), а не модуля аутентификации.

  • 9.2.1 Эффективная метка всегда пересекается с потолком пользователя из бэкенда (МНКЦ, get_user_mnkc) — потолок задаёт система, не сертификат. См. свободную функцию compute_effective_label (mac/orchestrator.rs).
  • 9.2.2 cert_integrity = "required" отвергает сертификаты без расширения; stub-бэкенд отказывается стартовать с required.
  • 9.2.3 Парсер IntegrityLabel::from_der strict: проверка длин, отсутствие trailing bytes, BIT STRING unused-bits ≤ 7. Битый DER → отказ + аудит-событие cert_max_integrity_parse_failed.
  • 9.2.4 Запись sessions.json идёт через openat(O_TMPFILE)fchmodpdp_set_fd(label)linkat/rename атомарно, лейбл накладывается на fd до публикации имени. irelax через fd-based API ядро не принимает (EINVAL) — relax-семантика для sessions.json обеспечивается iinh-наследованием от parent dir /run/tessera/ (tmpfs).
  • 9.2.5 postinst накладывает chattr +i на host_id после первой записи, сам файл лежит в каталоге /var/lib/tessera/ (0750 root:tessera, см. debian/postinst). Writable-state демона изолирован в /var/lib/tessera/daemon/ (0750 tessera:tessera), поэтому компрометация демона не позволяет подменить доверенные роли или теги через их родительский каталог.
  • libparsec parsec_capget symbol-сонейм не зафиксирован публично; build.rs не линкует libparsec-base3 по умолчанию. Если на конкретном Astra-релизе сборка выдаст «undefined symbol», нужно добавить libparsec-base3 в debian/control и cargo:rustc-link-lib=parsec-base в build.rs.
  • libpdp.so.3 — proprietary, без публичного fuzzing-покрытия. Защита: LD_LIBRARY_PATH фиксирован, ABI обёрнут в return-pointer signature (см. 5337fea), все вызовы — под panic=abort.
УгрозаТестФайл
9.1.1intersect(cert, caps) capping levelcrates/tessera_core/tests/mac_orchestrator.rs
9.1.2cert_integrity=required rejects no-ext leafcrates/pam_tessera/tests/mac_open_session.rs::open_session_fails_when_required_but_cert_lacks_ext
9.1.3malformed DER → parse-failed eventcrates/tessera_core/tests/cert_extensions_parse.rs
9.1.4fd-based irelax label on atomic writecrates/tessera_core/tests/mount_guard_tmpfs.rs
9.1.5host_id immutability after installE2E manual: vagrant/scripts/test-mac.sh T12

10. Реестр угроз (систематический проход 2026-06)

Заголовок раздела «10. Реестр угроз (систематический проход 2026-06)»

Результат полного bootstrap-прохода по коду @ 14b828e (исследовательский swarm: docs/поверхности/активы/инфраструктура/git-история, затем кластеризация и STRIDE gap-fill) с уточнением у владельца. Формат совместим со schema THREAT_MODEL.md (downstream-инструменты парсят таблицу регексом). Угроза — это класс, переживающий патч конкретного бага; найденные баги указаны в evidence и лишь повышают likelihood.

Статический триаж-проход (2026-06-06, 25 сырых находок → 5 подтверждённых) верифицировал инстансы этого реестра. Важная ось: impact/likelihood в реестре — оценка класса угрозы в наихудшем сценарии; конкретный инстанс при стекe прекондиций может быть instance-severity LOW при class-impact critical. Оба числа нужны: класс — приоритет инвестиций в митигации, инстанс — срочность патча. Проход также вскрыл два класса, отсутствовавших в bootstrap: ложный отзыв через issuer-scope (добавлен в T1/T8) и path-confusion на смонтированном носителе (новый T12).

Этот раздел дополняет §3–§6: §3 описывает механизмы защиты per-угроза, здесь — единый ранжированный реестр (impact × likelihood, по убыванию).

idthreatactorsurfaceassetimpactlikelihoodstatuscontrolsevidence
T1Вход по отозванному удостоверению: остаточная деградация отзыва (просроченная CRL пропускается при crl_strict=false — «отзыв не вечен»; CRL без nextUpdate ограничена только opt-in crl_max_age_hours)insiderОбработка CRLРешение об аутентификации, состояние отзываcriticalpossiblepartially_mitigatedкороткий TTL удостоверений ограничивает окно; подпись CRL проверяется обязательно (fail-closed, crl/store.rs); OCSP реализован (ocsp/crl_then_ocsp, nonce, issuer-signer проверка); пустой CRL-store при mode=crl отклоняется на конструировании verifier’а (2026-07); crl_strict=true opt-in; issuer-DN binding в check_revocation (RFC 5280 §6.3.3) — CRL применяется только к сертификатам своего издателя14b828e, openspec/revocation (закрыто: обязательная проверка подписи CRL; issuer-DN binding против ложного отзыва при cross-CA коллизии серийников; тихий дефолт «отзыв не проверяется» устранён)
T2Обход авторизационной политики через fail-open дефолты и тихие fallback-путиinsiderПроверка цепи и challenge-response; Парсинг X.509/PKCS#12 с носителя; config.tomlРешение об аутентификации, fail-closed инвариантcriticalpossiblepartially_mitigatedmandatory-extension policy (host_binding/allowed_roles); тихий fallback-путь допуска устранён с 2026-07: расширение pam_cert_user_binding и секция [[user_mapping]] удалены, допуск решается только allowed_roles верифицированного удостоверения, а конфиг с удалённой секцией отвергается при валидации; строгий DER-парсинг МКЦ-меток; пустой/опущенный sig-whitelist с 2026-06 подменяется безопасным дефолтом (SHA-256/384/512 RSA + ECDSA, без SHA-1/ГОСТ) на этапе валидации конфига — accept-all дефолт устранён; extractable PKCS#11-ключ с 2026-06 отклоняется по умолчанию (ExtractableKeyRejected), WARN остаётся только при явном opt-in pkcs11_allow_extractable_keys = true; с 2026-08 закрыт и парный fail-open: молчание провайдера больше не читается как CKA_EXTRACTABLE = FALSE — токен, не сообщивший атрибут, отклоняется отдельной ошибкой ExtractableAttributeUnavailable со своим opt-in pkcs11_allow_unreported_extractable, поскольку cryptoki молча выбрасывает из результата чтения атрибуты, которые токен отказался отдать; пропуск секции [trust.revocation] или ключа mode с 2026-07 — ошибка валидации конфига (тихий дефолт «отзыв не проверяется» устранён; none выбирается только явно)pre_validate.rs:28, flow.rs:662, key_lookup.rs, config/validated.rs (закрыто: тихий дефолт «отзыв не проверяется» устранён — revocation-mode обязателен)
T3RCE/повреждение памяти в root-логин-процессе при парсинге злонамеренного носителя: DER/PKCS#12 в OpenSSL до верификации и образ ФС в ядре при mount(2)local_userПарсинг X.509/PKCS#12 с носителя; USB mountHost process integritycriticalpossiblepartially_mitigatedRust-обвязка; panic guard (не спасает от UB в C); mount с nosuid,nodev,noexec; история CVE парсеров ASN.1/ФС-драйверов как прецедент
T4Evil-maid вне Astra: подмена config.toml, нативных .so (PKCS#11/gost-engine), host_id — без МКЦ-меток, DIGSIG и immutable-бита Debian/Ubuntu защищены только DAClocal_userДинамическая загрузка нативного кода; config.toml; Host identity резолв; Установка/удаление пакетаHost process integrity, конфигурация, host identitycriticalpossiblepartially_mitigatedна Astra: МКЦ ilevel=63, chattr +i, DIGSIG/ЗПС; вне Astra: 0640/0750 root:tessera
T5Компрометация цепочки сборки/поставки: бэкдор в .so логин-стека всего парка через незапиненную базу builder-image, инструменты без checksum, неподписанные .deb, crates.io-зависимостьsupply_chainCI / supply chain сборки; Зависимости CargoЦелостность release-артефактов, Host process integritycriticalpossiblepartially_mitigatedreproducible build (.buildinfo), rust-cache pinned by hash, cargo-deny, draft-релизы; Astra DIGSIG отклонит неподписанный .so; GPG-подпись .deb и apt-репозиторий запланированы
T6Нарушение memory-safety на PAM FFI границе: manual ownership AuthContext (Box::into_raw), conv-указатели, unsafe-блоки cdyliblocal_userPAM ABI (pam_sm_*); PAM data (AuthContext между фазами)Host process integritycriticalrarepartially_mitigatedRust; panic_guard → PAM_AUTHINFO_UNAVAIL; forbid(unsafe) в proto; no-alloc между fork/execve
T7Сессия переживает извлечение носителя: путь мониторинга не fail-closed (SessionOpen-ошибка не фатальна при strict)local_userIPC сокет monitord; udev события; sessions.json персистАктивная сессия, контроль извлечения носителяhighlikelypartially_mitigatedudev REMOVE → grace → lock/logout/hook/shutdown; race-check на SessionOpen; фикс XDG_SESSION_ID-пути в 0.3.13; с 2026-06 Lock/Logout без logind id fail-closed: error-лог + reboot хоста (сессия уничтожается, машина возвращается на экран входа) вместо тихого дропа действияflow.rs:742, actions.rs:51 (закрыто: Lock/Logout без logind id теперь fail-closed — reboot вместо тихого дропа действия)
T8Недоступность устройства (DoS/lockout): cert-only без rescue-канала, намеренная блокировка чужого токена (3 PIN-попытки), USB-timeout, monitord strict, ложный expiry при дрейфе часов, ложный отзыв валидного удостоверения при cross-CA коллизии серийников (issuer-DN не проверяется)local_userPAM ABI (pam_sm_*); IPC сокет monitord; udev события; Обработка CRLРешение об аутентификации (availability)highlikelypartially_mitigatedрежимы 2fa/optional с парольным fallback; rescue-канал для cert-only задокументирован, но не enforce’ится; grace-окна; clock_skew-допуск в acct_mgmt (lib.rs acct_mgmt_core)закрыто: issuer-DN binding в check_revocation (устраняет ложный отзыв при cross-CA коллизии серийников)
T9Некорректный PAM-стек после установки/обновления/удаления: bypass (остатки optional-режима, недочищенные @include) или полный lockoutlocal_adminУстановка/удаление пакета (postinst/postrm, integrate-pam.sh, finish-bootstrap.sh); PAM ABIPAM-стек системы, Решение об аутентификацииhighpossibleunmitigatedbackup bak.; интерактивный y/N в finish-bootstrap; синтаксической валидации PAM после правки нет
T10Эскалация привилегий через hook-механизм: PAM-derived переменные в окружении child-процессаlocal_userХуки fork+execveHost process integrity, активная сессияhighrarepartially_mitigatedplaceholders как отдельные argv (без shell), setuid/setgid в child, таймауты; с 2026-06 pre-exec проверка прав hook-файла и всех родительских каталогов (как sudo/ssh): S_IWOTH — всегда отказ, S_IWGRP — отказ кроме группы root/egid, нечитаемый stat — отказ (fail-closed); остаточное TOCTOU-окно stat→execve эквивалентно sudo/sshзакрыто: pre-exec валидация прав hook-файла и родительских каталогов, validator.rs:71, child_setup.rs:335
T11Разведка через раскрытие состояния: реестр сессий (кто залогинен), диагностика на экране входа, аудит-журналlocal_usersessions.json персист; PAM ABIРеестр сессий, аудит-событияlowpossiblepartially_mitigated0660/0750, tmpfs; диагностика «выпущен для другого устройства» — осознанный UX trade-off
T12Path-confusion на смонтированном USB-носителе: symlink в ext4 (или другой symlink-capable FS) на USB → чтение файлов хоста под root; MS_NOSYMFOLLOW не выставлен в mount-флагахphysical_userUSB mount; tessera_core/discovery.rsКонфиденциальность файлов хоста (частичная)mediumrarepartially_mitigatedс 2026-06 discover_credentials делает canonicalize + boundary check (starts_with канонического mountpoint) для p12 и chain.pem; выход за mountpoint → отказ (EscapesMount, fail-closed); закрывает и mid-path symlink, и ..; MS_NOSYMFOLLOW в mount-флагах по-прежнему не выставлен (defense-in-depth, открыто)закрыто: canonicalize + boundary check в discover_credentials, discovery.rs:92, usb.rs:85
threatreason
Spoofing/Tampering сетевых каналовУстройства офлайн, сетевых интерфейсов входа нет
Root на устройстве, полный офлайн-доступ к дискуВне модели по построению — слой целостности среды (см. §2, §4.1)
Repudiation многопользовательских действийЧастично закрыто структурированным аудитом; корреляция с серверной сессией — задача серверной части, не PAM-модуля
Подделка udev-событийТребует root или CAP_NET_ADMIN на netlink — эквивалент root-нарушителя
Side-channel/HW-атаки на токенСвойство сертифицированного носителя (СКЗИ), вне TOE (см. §4.3, §4.6)
Timing-атаки на challenge-responseПодпись выполняет OpenSSL/токен; secret-зависимых сравнений в коде tessera не найдено
DoS физическим повреждением устройстваВне модели (физзащита среды)
mitigationthreat_idscloses_classeffortстатус
Новая CRL-семантика: отзыв вечен для серийников из просроченной CRL; просроченность бьёт только доверие к полноте списка + audit-событиеT1yesMчастично закрыто 2026-06/07: verify_signature обязателен, OCSP реализован, пустой store при mode=crl — fail-closed; ОТКРЫТО только «отзыв вечен для просроченной CRL» (stale-CRL скипается при crl_strict=false)
Конфиг-инвариант fail-closed: пустой sig-whitelist = ошибка валидации; отсутствие revocation-mode = ошибка валидации; malformed binding-расширение = отказ (не fallback); CKA_EXTRACTABLE=true = блок при hardware-policyT2yesSsig-whitelist реализован 2026-06 (вариант: безопасный дефолт вместо ошибки валидации — не ломает существующие конфиги); revocation-mode обязателен с 2026-07 (пропуск секции/ключа = ошибка валидации, тихий дефолт none устранён); EXTRACTABLE реализован 2026-06; binding-fallback — открыто
Привести код к docs в мониторинге: strict → SessionOpen-ошибка фатальна; сессия без logind id — деградация в tty-target вместо дропа действияT7yesSlogind-id-часть реализована 2026-06 (вариант: fail-closed reboot вместо tty-деградации); strict→fatal — открыто
Подпись .deb (GPG) + pin базового образа builder по digest + checksum для curl-загружаемых инструментовT5yesSоткрыто
Пост-валидация PAM-конфига после каждой правки (синтакс-чек, smoke-тест) + атомарный rollback в integrate-pam.shT9yesMоткрыто
Debian-профиль целостности: dpkg-statoverride на критичные пути, рекомендация AIDE/IMA, проверка прав hook-файлов на старте демонаT4, T10partialMhook-права реализованы 2026-06 (вариант: pre-exec проверка в fork_exec, строже чем на старте демона); остальной профиль — открыто
Изоляция парсинга носителя: privilege-separated helper для PKCS#12/DER до верификации; размерные лимитыT3partialLоткрыто (лимиты p12/chain есть)
проверка rescue-канала в finish-bootstrap.sh для cert-only (clock_skew-допуск в acct_mgmt — сделано)T8partialSоткрыто
MS_NOSYMFOLLOW в mount(2) + canonicalize/boundary check в discover_credentials перед fs::readT12yesScanonicalize/boundary check реализован 2026-06; MS_NOSYMFOLLOW — открыто (defense-in-depth)
CRL issuer-DN binding в check_revocation: сравнивать issuer_dn_der cert’а с issuer CRL до match по серийнику (RFC 5280 §6.3.3)T1, T8yesSреализовано 2026-06

11. Инструменты выпуска (tessera_issuer, issuer-tooling 2026-07)

Заголовок раздела «11. Инструменты выпуска (tessera_issuer, issuer-tooling 2026-07)»

Расширение TOE: клиентский инструментарий выпуска сертификатов — ядро tessera_issuer и CLI issuer. Инструмент не кастодиан: с бэкендами pkcs11 и vault приватные ключи живут в токене/HSM/Vault Transit и через код выпуска не проходят. Исключение — явный файловый бэкенд (file): ключ из PKCS#8-файла загружается в память процесса выпуска (см. 11.1.7 и 11.3). Канон требований — openspec-спеки cert-issuance, issuer-signing, issuer-cabinet, issuance-journal.

Поверхность выпуска из браузера перенесена в коммерческий инструментарий. Локальный агент issuer serve (мост браузер ↔ бэкенд подписи) и статический веб-кабинет поставляются в составе коммерческих инструментов и из открытого репозитория больше не собираются. Их анализ сохранён ниже (строки 11.1.1–11.1.3, 11.1.5 и пункт про подменённый кабинет в 11.3), потому что поверхность по-прежнему существует в продукте; она просто вне периметра открытого репозитория, который поставляет CLI и бэкенды pkcs11/vault/file.

#ПоверхностьУгрозаЗащита
11.1.1HTTP-агент issuer serve (127.0.0.1)CSRF / DNS rebinding из браузера оператора; произвольный локальный процесс просит подписьbind строго на 127.0.0.1; парный токен сессии (CSPRNG, сравнение константного времени) — основной гейт: запрос без валидного токена отвергается до обращения к модулю подписи; Origin вторичен — присутствующий Origin должен быть в allowlist (чужой Origin и DNS-rebind отсекаются), отсутствующий (легитимный same-origin GET) гейтит токен; cross-origin страница не прочитает вшитый токен и не поставит его заголовок без preflight; PIN по HTTP не принимается — поля нет в протоколе; подтверждение каждой операции оператором в интерфейсе агента с показом разобранных полей TBS (агент — доверенный дисплей; токен аутентифицирует кабинет, подтверждение авторизует операцию)
11.1.2PIN ключа выпускаутечка через логи, argv, журнал, сообщения об ошибкахввод только на стороне агента (терминал/pinentry); Secret+zeroize на время операции; отсутствует в Debug/Display ошибок и в журнале выпусков
11.1.3Статика веб-кабинета (SPA + WASM)подмена артефакта при доставкекабинет — внешний статический бандл, агент раздаёт его из --cabinet-dir same-origin по локали 127.0.0.1 (либо своя статика в закрытом контуре); целостность бандла — за его поставкой (манифест SHA256SUMS, доверенный канал доставки), не за бинарём агента; ключи недостижимы даже из подменённого SPA (подпись за агентом + токеном). Остаточный риск — см. 11.3
11.1.4Vault/OpenBao TransitTransit подписывает любой присланный TBSвсе проверки выпуска выполняются до подписи на клиенте; политика Vault ограничивает, кто может звать sign; токен Vault не логируется
11.1.5Снапшот инвентаря для формподложный снапшот подставляет чужие устройства/роли в формыподпись снапшота проверяется, битая → отказ; неподписанный снапшот явно помечается как ручной, показывается возраст
11.1.6Журнал выпусковсокрытие/переписывание факта выпускаhash-chain с фиксированным genesis + подпись головы; запись до выдачи артефакта (fail-closed); вторичен к первичной правде — аудиту входов на устройствах
11.1.7Файловый ключ CA (--backend file)кража файла ключа с диска; подбор пароля офлайн; утечка пароляправа файла проверяются до чтения (доступ группе/остальным → отказ); рекомендация — зашифрованный PKCS#8 (PBES2), незашифрованный принимается только с предупреждением при каждом старте; пароль — pinentry/env, Secret+zeroize, не в argv/логах; расшифрованный буфер затирается после загрузки. Класс защиты ниже токена/HSM/Vault — см. 11.3

11.2 Что ограничивает ущерб при злоупотреблении выпуском

Заголовок раздела «11.2 Что ограничивает ущерб при злоупотреблении выпуском»

Монотонное сужение рамок делегирования проверяется на стороне выпуска тем же разделяемым кодом (tessera_ext), что и enforcement Engine: даже полностью скомпрометированный инструмент выпуска не может выдать сертификат шире рамок родительского CA — Engine отвергнет его на устройстве (AND/MIN-семантика рамок, §3.8). Серийники случайные 128-битные — сговор по серийникам между операторами не требуется и невозможен как канал.

  • Компрометация машины оператора = компрометация сессии выпуска: атакующий с правами оператора может выпустить сертификаты в пределах рамок предъявленного CA (но не украсть ключ — он в токене/HSM; и не выйти за рамки — см. 11.2). Паритет с 4.1 (компрометация хоста вне TOE).
  • Файловый ключ при компрометации хоста: с --backend file ключ CA, в отличие от токена/HSM/Vault, извлекаем — атакующий с доступом к файлу (и паролю или памяти процесса) уносит сам ключ, а не только сессию выпуска. Файловый бэкенд — принятый риск для стендов, CI и малых инсталляций; прод-рекомендация — PKCS#11 или Vault Transit. Рамки делегирования при этом всё равно держат: украденный ключ CA организации не позволяет выйти за его envelope (11.2), украденный корень — катастрофа того же класса, что 2.3.
  • Подменённый кабинет подсовывает иной TBS — закрыто обязательным подтверждением операции (11.1.1): агент разбирает TBS и показывает subject/срок/роли/рамки в доверенном канале до запроса PIN; оператор подтверждает то, что реально подписывается. Остаётся невнимательность оператора (подтвердил не глядя) — организационная мера.
  • Four-eyes на чистом клиенте: standalone-путь выпуска не обеспечивает второй подписи по построению; это зона ответственности организации (фиксируется явно, серверный путь — вне scope открытой части).
  • Зажатый CRL: инструмент выпускает CRL, но не доставляет его; backstop — короткие TTL листов (семантика revocation).