MAATRIX / Блог / Хеши посчитали один раз и забыли: зачем нужен манифест целостности

Хеши посчитали один раз и забыли: зачем нужен манифест целостности

MAATRIX

Вы наверняка делали это хотя бы раз: перед архивацией важных данных прогнали sha256sum -r . > checksums.txt, положили файл рядом с бэкапом «для порядка» и на этом успокоились. Полгода спустя один файл в архиве оказывается битым — а сверять его было не с чем, потому что сам checksums.txt лежал в том же архиве и сгнил вместе с данными, или его никто ни разу не открывал заново. Манифест целостности решает именно эту проблему, но только если относиться к нему как к живому процессу, а не как к разовой бумажке для галочки.

Почему разовый хеш бесполезен

Контрольная сумма, посчитанная один раз, отвечает только на один вопрос: «каким был файл в момент X». Она ничего не говорит о том, что происходит с файлом дальше. Через месяц, год или пять лет у вас на руках останется число, с которым никто и никогда не сравнивал текущее состояние данных — по сути, случайная строка в файле, которая создаёт иллюзию защиты.

Типичный сценарий: администратор настраивает бэкап, при первом запуске скрипта заодно считает md5sum для всех файлов и сохраняет вывод. Задача выполнена, галочка поставлена, отчёт руководству отправлен. Дальше скрипт бэкапа продолжает работать годами, а хеш-файл так и остаётся артефактом одного дня — новые версии данных с ним никто не сверяет, потому что сверка не встроена ни в один процесс. Когда через два года всплывает битый файл, выясняется, что манифест устарел ещё в момент второго изменения данных, то есть почти сразу.

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

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

Что такое манифест целостности на практике

Манифест целостности — это отдельный от самих данных реестр вида «путь к файлу — алгоритм — контрольная сумма», который:

  • создаётся при первом полном учёте данных (baseline);
  • хранится отдельно от защищаемых файлов — на другом томе, в другой системе, в git-репозитории или у стороннего сервиса;
  • регулярно сверяется с текущим состоянием данных по расписанию, а не «когда вспомнили»;
  • обновляется контролируемо при каждом легитимном изменении данных — не автоматически при любом изменении, а именно при осознанном.

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

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

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

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

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

Формат манифеста: просто и машиночитаемо

Не нужно изобретать структуру — берите то, что умеют читать стандартные утилиты. Самый переносимый вариант — обычный текстовый вывод sha256sum:

# manifest-2026-08-28.sha256
3f786850e387550fdab836ed7e6dc881de23001  ./contracts/2026/dogovor-01.pdf
a94a8fe5ccb19ba61c4c0873d391e987982fbbd  ./contracts/2026/dogovor-02.pdf
e3b0c44298fc1c149afbf4c8996fb92427ae41e  ./db-dumps/prod-2026-08-28.sql.gz

Такой файл проверяется одной командой:

cd /data
sha256sum -c /secure/manifests/manifest-2026-08-28.sha256

Утилита сама пройдётся по списку, пересчитает суммы и выведет OK или FAILED для каждой строки. Ничего изобретать не нужно — формат читают sha256sum, md5sum, shasum на macOS и десятки готовых инструментов мониторинга целостности вроде AIDE и Tripwire.

Если нужны метаданные (размер, дата изменения, кто добавил запись), удобнее JSON-манифест:

{
  "generated_at": "2026-08-28T09:00:00Z",
  "algorithm": "sha256",
  "files": [
    {
      "path": "db-dumps/prod-2026-08-28.sql.gz",
      "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e...",
      "size_bytes": 483920112,
      "added_by": "backup-cron"
    }
  ]
}

JSON проще парсить в скриптах сверки и хранить историю версий манифеста (git отлично для этого подходит — diff покажет, какие именно файлы поменялись между двумя baseline). Но для больших деревьев файлов чистый sha256sum-формат работает быстрее и требует меньше кода.

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

Где хранить манифест отдельно от данных

Правило простое: манифест должен пережить любой сценарий, при котором портятся сами данные. Варианты по нарастанию надёжности:

Где хранитьПлюсыМинусы
Отдельный раздел на том же сервереПросто настроитьНе спасёт при отказе всего сервера
Отдельный сервер (например, сервер мониторинга)Переживёт отказ основного сервераТребует сетевого доступа и синхронизации
Git-репозиторий (приватный)Версионирование, diff между baseline бесплатноНе годится для очень больших списков файлов без LFS
Объектное хранилище (S3-совместимое, другой регион)Географическая изоляцияДополнительные расходы и настройка доступа
Печатная копия / офлайн-носитель для критичных baselineНе тронет ни один сетевой инцидентНеудобно для частой сверки, годится только как последний рубеж

Практичная связка для VPS — держать манифест в git-репозитории на отдельном сервере или в приватном Gitea/GitLab, куда cron-джоба коммитит новый снимок после каждой контролируемой смены данных. Тогда история изменений манифеста сама становится журналом легитимных изменений: если в логах git нет коммита, а данные изменились — это подозрительное расхождение, достойное разбора.

Baseline и обновление: где проходит грань легитимности

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

Работающая схема:

  1. Baseline — первичный полный расчёт после того, как данные заведомо в порядке (например, сразу после восстановления из проверенного бэкапа или после ручного аудита).
  2. Плановая сверка — по расписанию (cron/systemd timer) пересчитываются текущие суммы и сравниваются с манифестом. Расхождение — это событие, а не автоматическая команда «обнови манифест».
  3. Разбор расхождения — человек (или отдельный процесс с логированием причины) решает: это легитимное изменение (файл действительно поменяли по делу) или нет.
  4. Контролируемое обновление — только после разбора манифест обновляется, причём с фиксацией причины: кто, когда, почему. Простой git-коммит с осмысленным сообщением решает эту задачу без лишней инфраструктуры.

Пример systemd-таймера для регулярной сверки:

# /etc/systemd/system/integrity-check.service
[Unit]
Description=Integrity manifest check

[Service]
Type=oneshot
ExecStart=/usr/local/bin/check-integrity.sh
# /etc/systemd/system/integrity-check.timer
[Unit]
Description=Daily integrity manifest check

[Timer]
OnCalendar=*-*-* 03:30:00
Persistent=true

[Install]
WantedBy=timers.target

А сам check-integrity.sh может выглядеть так — с явным разделением «сверка» и «обновление»:

#!/usr/bin/env bash
set -euo pipefail

DATA_DIR=/data
MANIFEST=/secure/manifests/current.sha256
REPORT=/var/log/integrity-check.log

cd "$DATA_DIR"
if sha256sum -c "$MANIFEST" > "$REPORT" 2>&1; then
  logger -t integrity-check "OK: все файлы совпали с манифестом"
else
  logger -t integrity-check "FAILED: расхождение, см. $REPORT"
  # намеренно НЕ обновляем манифест автоматически —
  # уведомление уходит человеку на разбор
  mail -s "Integrity check FAILED on $(hostname)" admin@example.com < "$REPORT"
fi

Обратите внимание: скрипт не трогает манифест при расхождении, он только сигнализирует. Обновление манифеста — отдельная ручная (или отдельно логируемая) команда, которую вы запускаете осознанно после того, как убедились, что изменения легитимны:

cd /data
sha256sum -r . > /secure/manifests/current.sha256
cd /secure/manifests
git add current.sha256
git commit -m "baseline update: плановая ротация логов 2026-08-28, изменения ожидаемы"

Что стоит включать в манифест, а что нет

Манифест на все файлы подряд обычно избыточен и медленно проверяется. Разумная практика — выделить категории данных по критичности:

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

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

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

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

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

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

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

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

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

Чем манифест целостности отличается от обычного бэкапа с проверкой хешей внутри архиватора?

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

Какой алгоритм хеширования выбрать — MD5, SHA-1 или SHA-256?

Для контроля случайной порчи (не защиты от намеренной подделки) подойдёт любой, включая MD5 — задача найти совпадение случайно испорченного файла с оригиналом, а не противостоять атакующему. Но раз MD5 и SHA-1 давно считаются криптографически слабыми и не требуют дополнительных вычислительных затрат по сравнению с SHA-256 на современном железе, разумнее сразу использовать SHA-256 — так манифест годится и для сценариев, где важна защита от намеренной подмены.

Нужно ли сверять манифест на каждом чтении файла или это слишком дорого?

Обычно нет смысла — сверка на каждое чтение съедает I/O и CPU без ощутимой пользы для большинства сценариев. Плановая сверка раз в сутки или несколько раз в неделю для большинства данных достаточна; для по-настоящему критичных файлов (ключи, конфиги продакшена) можно поставить более частый интервал именно для этой узкой категории.

Что делать, если манифест сам оказался повреждён или потерян?

Именно поэтому его нужно хранить отдельно и желательно версионировать (git, объектное хранилище с версионированием). Если манифест утрачен полностью, придётся заново создавать baseline — но тогда вы теряете историю и не сможете определить, когда именно данные разошлись с последним валидным состоянием.

Можно ли автоматизировать разбор расхождений, чтобы не дёргать человека каждый раз?

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

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

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

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