MAATRIX / Блог / Приватный ключ в бэкапе на общем диске: как оценить масштаб

Приватный ключ в бэкапе на общем диске: как оценить масштаб

MAATRIX

Кто-то из команды делал ревизию бэкапов, распаковал архив трёхмесячной давности и наткнулся на файл id_rsa без пароля — прямо в составе дампа /home одного из серверов. Архив лежал на сетевом диске, к которому теоретически имел доступ весь отдел разработки, а по факту — ещё и пара подрядчиков, чей доступ никто вовремя не отозвал. Первая реакция обычно паническая: перевыпустить всё немедленно. Вторая, более трезвая — сначала понять, что вообще произошло и насколько это плохо, а потом уже действовать. Ниже — методика, которая отвечает на вопрос «насколько плохо» конкретными шагами, а не интуицией.

Почему нельзя просто пожать плечами

Первый импульс при находке ключа в старом бэкапе — «ну лежал и лежал, наверное, никто не смотрел». Это ровно та логика, которая превращает мелкий косяк с гигиеной хранения в серьёзный инцидент задним числом. Секрет в бэкапе — это не абстрактный риск «а вдруг», а конкретный файл, который физически находился в месте, доступном для чтения кем-то, кого вы не контролировали в момент, когда там появился ключ.

Разница между «ключ был доступен» и «ключ был использован» огромная, но на старте расследования вы не знаете, на какой вы стороне. Поэтому единственный безопасный подход — считать секрет скомпрометированным по умолчанию, пока не будет доказательств обратного, а не наоборот. Это не паранойя ради паранойи: перевыпуск ключа стоит пятнадцать минут работы, а разбор последствий реальной компрометации — дни, а если ключ давал доступ к продакшену с правами на запись — то и потерю данных или репутации. Стоимость ошибки асимметрична, значит и решение должно быть перекошено в сторону безопасности.

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

Шаг 1: зафиксируйте сам факт находки, не трогая ничего лишнего

Прежде чем удалять файл, менять права или паниковать в чате команды, зафиксируйте состояние как есть:

# Точный путь и метаданные файла
stat /mnt/backup-share/archive-2026-05/home/deploy/.ssh/id_rsa

# Контрольная сумма — пригодится, если понадобится сверить с другими копиями
sha256sum /mnt/backup-share/archive-2026-05/home/deploy/.ssh/id_rsa

# Кто и когда последний раз читал/менял файл на уровне ФС (если это не Windows-шара со сброшенными атрибутами)
stat -c '%X %Y %Z %n' /mnt/backup-share/archive-2026-05/home/deploy/.ssh/id_rsa

%X — время последнего доступа (atime), %Y — время модификации (mtime), %Z — время изменения метаданных (ctime). На сетевых хранилищах atime часто отключён из соображений производительности (опция монтирования noatime — обычная практика, снижающая нагрузку на диск при интенсивном чтении), так что не удивляйтесь, если atime окажется бесполезным — тогда единственный источник о факте чтения файла не файловая система, а внешние логи доступа.

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

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Шаг 2: восстановите, кто вообще имел доступ к хранилищу

Это центральный вопрос всей оценки. Задача — построить список «кто мог прочитать этот файл, пока он там лежал», причём не на сегодняшний день, а ретроспективно, на весь период существования секрета в этом месте.

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

Что проверять в зависимости от типа хранилища:

  • NFS/Samba-шара на своём сервере. История изменений /etc/exports или конфига Samba в системе контроля версий (если конфиги вообще версионируются — если нет, это тоже вывод для отчёта). Список пользователей и групп ОС, у которых были права на директорию, и когда эти права менялись — смотрите ausearch или системные логи, если включён аудит (auditd), либо историю chmod/chown в истории команд администраторов, если она сохранилась.
  • S3-совместимое объектное хранилище (MinIO, Ceph RGW, облачный S3). Политики бакета (bucket policy) и ACL объекта на момент загрузки — если версии политик не сохранялись явно, ищите в CloudTrail или аналогичном логе действий API вызовы PutBucketPolicy/PutBucketAcl за весь период. Отдельно проверьте, не был ли бакет когда-либо публично читаемым (public-read) — это частая причина, по которой «внутренний» бэкап оказывается доступен снаружи, разбор похожего случая есть в статье про хранилище бэкапов, которое было доступно проду на запись — там показано, как небрежная политика доступа расползается за пределы того круга людей, для которого задумывалась.
  • Внутренний файловый сервер с доступом через VPN. Список активных подключений и то, кто в принципе имел действующий VPN-доступ в интересующий период. Если VPN-доступ был у бывших сотрудников или подрядчиков дольше, чем нужно, это отдельная находка, которую тоже стоит зафиксировать в отчёте.

Результат этого шага — не точный список из трёх фамилий (так обычно не бывает), а более широкий, но конкретный круг: группа AD/LDAP из N человек, диапазон дат, когда группа менялась, и список внешних учёток (подрядчики, партнёры), у которых теоретически был доступ. Чем шире круг, тем консервативнее должна быть дальнейшая оценка риска.

Шаг 3: поднимите логи доступа, если они есть — и честно признайте, если их нет

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

Что стоит проверить:

# Samba: журнал аудита VFS, если модуль full_audit подключён
grep "id_rsa" /var/log/samba/audit.log

# NFS: аудит через auditd, если настроено правило на директорию
ausearch -f /mnt/backup-share/archive-2026-05 -ts today

# S3-совместимое хранилище: серверные логи доступа к бакету (если включено логирование)
aws s3api get-bucket-logging --bucket backups-internal

# Локальный веб-сервер, отдающий файлы (если бэкапы раздавались через nginx/Apache)
grep "archive-2026-05" /var/log/nginx/access.log*

Здесь чаще всего расследование упирается в неприятную правду: логирование доступа к файловому хранилищу или объектному бакету попросту не было включено, потому что это «внутреннее» хранилище и никто не думал, что понадобится аудит задним числом. Если логов нет — это не повод считать риск нулевым, это повод считать его максимальным в рамках того круга людей, который вы восстановили на шаге 2. Отсутствие доказательств доступа — не доказательство отсутствия доступа.

Если логи частично есть (например, только за последние 30 дней из-за ротации), явно укажите в отчёте временной разрыв: «за период с X по Y логов нет, за Y по сегодня — есть, событий чтения этого файла не зафиксировано». Точность формулировки важна и для внутреннего решения, и на случай, если придётся отчитываться перед клиентами или регулятором.

Шаг 4: оцените, сколько секрет пролежал и что он открывал

Дата появления файла в хранилище — не всегда дата первой ротации бэкапов. Часто скрипт бэкапа существовал и раньше, а конкретно этот секрет (ключ) появился в системе позже — например, ключ создали при разворачивании нового сервера, а бэкап /home включал его с тех пор.

Восстановите:

  • когда ключ был впервые создан на исходном сервере (ls -la ~/.ssh/, дата создания файла на исходной машине, если она ещё существует, или дата в истории Ansible/Terraform, которая его разворачивала);
  • с какой периодичностью пересоздавался бэкап, в который он попадал, и сколько версий архива содержат этот файл — если ротация бэкапов хранит несколько поколений, секрет может быть размножен по десятку архивов за разные даты, и каждый — потенциально отдельная точка доступа;
  • что именно этот ключ открывал: доступ к одному серверу разработки или мастер-ключ, прописанный в authorized_keys сразу нескольких продакшен-хостов. Проверьте прямо на целевых серверах:
# На каждом сервере, куда теоретически мог быть прописан этот ключ
grep -f <(ssh-keygen -y -f id_rsa) ~deploy/.ssh/authorized_keys

Если публичная часть ключа находится в authorized_keys на нескольких машинах, масштаб инцидента умножается на число этих машин — секрет скомпрометирован не «немного», а полностью для каждого сервера, который ему доверяет. Проблема в таких случаях обычно не в самом факте существования старого ключа, а в том, что никто вовремя не проверил, где ещё он даёт доступ.

Шаг 5: примите решение по умолчанию — компрометация, пока не доказано обратное

После шагов 2–4 у вас есть три возможных исхода:

  1. Круг доступа узкий, логи есть, чтения файла не зафиксировано. Риск невысокий, но не нулевой — секрет всё равно нужно перевыпустить, потому что сам факт хранения открытого ключа в общем месте — нарушение практики, а не только конкретный инцидент.
  2. Круг доступа широкий или логов нет за часть периода. Считайте ключ скомпрометированным. Не потому, что есть прямые доказательства, а потому что бремя доказывания лежит на стороне «секрет цел», а не наоборот — а доказать это нечем.
  3. Есть признаки фактического использования (нетипичные подключения по этому ключу, активность с IP, которых не должно быть, действия в системе, которые никто из команды не подтверждает) — это уже не оценка риска, а разбор состоявшегося инцидента с полным протоколом реагирования: смена всех связанных учётных данных, проверка на закладки/бэкдоры на затронутых серверах, разбор логов на предмет дальнейшего продвижения атакующего.

Даже для исхода №1 перевыпуск ключа — не избыточная мера, а минимальная гигиена: цена ошибки (продолжать доверять потенциально скомпрометированному ключу) несопоставимо выше цены действия (сгенерировать новый ключ и раскатать его).

# Генерация нового ключа взамен скомпрометированного
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_new -C "deploy-key-rotated-2026-08"

# Раскатка нового публичного ключа на все затронутые серверы
ssh-copy-id -i ~/.ssh/id_ed25519_new.pub deploy@server1.example.com

# После проверки, что новый ключ работает — удаление старой записи
sed -i '/старый-отпечаток-ключа/d' ~deploy/.ssh/authorized_keys

Параллельно с перевыпуском ключа стоит удалить (а не просто «спрятать») сам секрет из всех копий бэкапа, где он найден, включая старые поколения архива, если это возможно без разрушения целостности остальных данных бэкапа — иначе задача повторится при следующей ревизии.

Правильная практика на будущее: шифрование и раздельное хранение секретов

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

Правильная схема строится на двух независимых принципах, и оба обязательны — один без другого не спасает:

ПринципЧто даётЧего не решает в одиночку
Шифрование бэкапаДаже при утечке доступа к хранилищу данные нечитаемы без ключаНичего не защищает, если ключ шифрования хранится там же, где и бэкап, — разбор этой ошибки есть в статье про бэкап, который был, а ключа шифрования не было
Отдельное хранение секретов (vault) вне обычных бэкаповСекреты вообще не попадают в общий архив, значит нечему утекать вместе с дампом /home или /etcНе защищает данные приложения — только сами секреты; для остального нужно шифрование бэкапа

Практические шаги для внедрения:

  • Исключите каталоги с секретами из обычного бэкапа. .ssh/, .aws/, .gnupg/, файлы *.pem, *.key, переменные окружения с токенами — не должны попадать в общий дамп домашних директорий или конфигов. В borgbackup, restic и большинстве инструментов есть механизм exclude-паттернов:
# restic: исключение приватных ключей и конфигов с секретами из бэкапа
restic backup /home/deploy \
  --exclude='**/.ssh/id_*' \
  --exclude='**/.aws/credentials' \
  --exclude='**/*.pem'
  • Секреты храните в специализированном хранилище с собственным контролем доступа и аудитом, а не в файлах на диске, который потом кто-то бэкапит скриптом на все случаи жизни. У такого хранилища есть встроенный журнал обращений, так что вопрос «кто читал этот секрет и когда» перестаёт быть задачей на детективное расследование постфактум.
  • Если секрет всё-таки должен попасть в бэкап (например, ключ шифрования самого репозитория бэкапов, который нужно суметь восстановить), шифруйте архив целиком отдельным ключом с оффлайн-хранением этого ключа — подробности в статье про настройку бэкапа с шифрованием на VPS.
  • Заведите регулярную ротацию секретов как процесс, а не как реакцию на инцидент — практика описана в статье про ротацию секретов и ключей. Плановая ротация раз в квартал делает окно потенциальной экспозиции конечным и предсказуемым, а не «неизвестно сколько месяцев назад».
  • Ограничьте доступ к хранилищу бэкапов принципом наименьших привилегий и включите логирование доступа заранее, а не после того, как понадобится разобраться в инциденте. Это часть общего чек-листа, с которым стоит свериться при подготовке инфраструктуры к аудиту безопасности.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Ключ был запаролен passphrase — это меняет оценку риска?

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

Можно ли определить, использовался ли ключ, по логам SSH на целевом сервере?

Частично да, если логи аутентификации хранятся достаточно долго (journalctl, /var/log/auth.log с ротацией на нужный период) и в них видно отпечаток использованного ключа (Accepted publickey for ... SHA256:...). Сверьте отпечаток из лога с отпечатком найденного ключа (ssh-keygen -lf id_rsa). Проблема в том, что логи обычно ротируются за недели, а не месяцы, так что для старых находок этот метод часто бесполезен — и это тоже нормально зафиксировать как «проверить не удалось» в итоговом отчёте.

Что если хранилище бэкапов физически недоступно снаружи компании — стоит ли вообще заморачиваться?

Стоит, потому что «недоступно снаружи» почти никогда не значит «доступно только тем, кому нужно». Внутренний периметр обычно шире, чем кажется: бывшие сотрудники с неотозванным VPN-доступом, подрядчики, скомпрометированная рабочая станция любого из тех, кто формально имел доступ. Оценка риска строится не по границе «внутри/снаружи», а по реальному кругу учётных записей с доступом.

Сколько времени в среднем занимает такое расследование?

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

Нужно ли уведомлять клиентов или регулятора, если ключ давал доступ только к внутренним dev-серверам?

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

Обсудить статью, задать вопрос или начать новую тему

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

Перейти в сообщество →