TL;DR
Если это СУБД с гитхаба, то никак.
Зачем вообще "надёжно" стирать данные? Главное же, чтобы пользователь через интерфейс СУБД не мог их достать. Мало ли, что там за остатки данных в файлах болтаются, это же не проблема. Или нет?
Вообще утечки данных через память и файлы иногда встречаются. Из-за этого есть требование затирать переменные, в которых хранились пароли или ключи. В таких ситуациях оптимизирующие иногда компиляторы добавляют багов, удаляя memset для буфера, который больше не используется.
А для баз данных, к примеру, есть требование ФСТЭК:
13.1. Система управления базами данных 6, 5 классов защиты самостоятельно или с применением сертифицированной операционной системы должна обеспечивать удаление баз данных и журналов, используемых системой управления базами данных, путем многократной перезаписи уничтожаемых (стираемых) объектов файловой системы специальными битовыми последовательностями.
Получается, что (сертифицированные) СУБД должны не просто помечать записи, как удалённые, но и перезаписывать удаляемые данные во всех местах, куда они были сохранины.
Нас заинтересовала эта тема, и мы решили потестировать публично доступные СУБД на предмет того, удаляют ли она данные, или что-то остаётся. И так как мы разрабатываем Natch - инструмент поиска поверхности атаки, то все проблемы выглядят как гвозди решили с помощью него исследовать потоки данных в СУБД.
Ведь работа с чувствительными данными и поиск утечек мало отличается от работы с недоверенными данными и поиска поверхности атаки.
Идея тестирования была такая:
Создаём базу данных.
Записываем туда "чувствительные" данные.
Отслеживаем поток этих данных в системе.
Удаляем данные средствами СУБД.
Проверяем те места, куда в п.3 данные попали, по-прежнему они там или нет.
Пишем статью на хабр.
PostgreSQL
Для того, чтобы проверить, удаляются ли данные, нужно понять, где они вообще хранятся. Конечно же, сначала надо создать базу данных:
CREATE DATABASE my;
USE my;
CREATE TABLE mytable (col text);
Natch умеет отслеживать "чувствительные" данные, поступающие из сети, или считанные из файла. Тогда можно либо подключаться к СУБД удалённо, помечая сетевой трафик (который и будет содержать добавляемые в базу данные). Либо записать команду INSERT в файл, и помечать для отслеживание уже содержимое файла.
Мы применяли второй способ, поэтому запускали PostgreSQL под контролем Natch, передавая в СУБД файл pg_insert.sql со следующим запросом:
INSERT INTO mytable VALUES (‘dhfgkjhewdrtg;kljhwekjthekjthkjwehtkjhertkjhewrkjthekjthkjwerhtkjhertkjhekljthekjgkjdsngkjnikjuh43kiujhtkj3ntkj3j3ntkj34hnrtkjh 3kjhtk3hrlkj oruq97g98weytg nui3ht k3h4trkhglkeoi;tru 8o934urowjrh qwtuikh3489tuyoiktghiljpoqwro;iwotrhyt’);
Добавляемая строка не очень случайная, но нам повезло, и она не сильно подверглась компрессии в хранилище. В итоге, Natch определил, что вставленные в таблицу данные осели в файлах data/base/16389/16390 и data/pg_wal/000000010000000000000001.
В первом файле хранятся сами данные строк таблицы, а второй - это write-ahead log (WAL). Это журнал изменений, который используется, по меньшей мере, для восстановления БД после сбоев.
Теперь, когда мы будем удалять данные, нужно убедиться, что они пропали из обоих файлов. Ведь это чувствительные данные, не хочется их разбрасывать где попало.
Тут уже понадобится небольшая хитрость. Ведь Natch отслеживает данные, пока они куда-то перемещаются или где-то обрабатываются. Но если данных больше нет, что он может показать? Для решения этой проблемы можно просто обратиться к содержимому файлов, где хранились чувствительные данные (с помощью find, grep или даже md5sum). И тогда либо мы увидим нужный паттерн в логе файловых транзакций, либо на диаграмме процессов появится стрелочка с передачей чувствительных данных в новый процесс.
Почему стрелочка может не появиться?
Дело в том, что Natch пока что не отслеживает распространение помеченных данных на дисковых накопителях. Поэтому если что-то записать на диск, а потом прочитать заново, то пометки будут потеряны.
Но если ОС оптимизирует этот процесс, и после записи сохранит данные в кэше в ОЗУ, то и пометки для них тоже сохранятся. В таких ситуациях поток данных Natch отслеживает успешно.
Итак, попробуем удалть данные из таблицы, а потом посчитать md5 для интересующих нас файлов. Утилита md5sum вынуждена будет прочитать все данные из файлов, а нам только это и нужно.
DELETE FROM mytable;
Команда DELETE наверняка не удаляет строки физически. Для полного удаления придётся сделать что-то ещё. Проверим эту гипотезу.
Файловые транзакции даже не пришлось смотреть, все данные оставались в кэше ОС. И действительно, из таблицы удалённые строчки не пропали. Но есть ещё команда VACUUM. Она-то точно удалит всё лишнее.
Данные всё ещё там, в WAL. Правда, уже в новом файле. А непосредственно из таблицы действительно всё удалилось.
Как ещё можно повлиять на БД? Удалить её целиком. Уж теперь-то точно всё должно пропасть.
DROP DATABASE my;
VACUUM FULL;
Для верности остановим СУБД, чтобы точно ничего не осталось в памяти. Ну и проверим, на месте ли добавленные данные, только в этот раз с помощью grep, а не md5sum:
systemctl stop postgresql
cd /usr/local/pgsql/data/base
grep -r oruq97g98
Итак, СУБД была остановлена, вся база удалена, а "чувствительные" данные не только остались в файле, но ещё и висели в файловом кэше ОС.
MySQL
Следующая СУБД для исследования - MySQL. С ней будем делать примерно то же самое. Сначала выясним, в какие файлы попадут наши данные после INSERT:
Тут, как и в postgres, кроме файла с таблицей, есть и дополнительные файлы с транзакциями - binlog и redo log.
Проверим, что будет после удаления данных из таблицы:
DELETE FROM mytable;
FLUSH TABLES;
FLUSH BINARY LOGS;
FLUSH ENGINE LOGS;
Данные ожидаемо сохранились в логах транзакций. Оказывается, binlog можно подчистить с помощью команды RESET BINARY LOGS AND GTIDS.
Остался redo log. Удаляем БД.
И опять из лога изменений данные стереть не удалось.
MariaDB
Третий наш испытуемый - СУБД MariaDB. Сначала выясняем, где хранятся добавляемые в таблицу данные.
Natch показывает запись только в logfile. Это довольно странно, потому что должен же ещё быть файл с таблицей. Погрепаем нашу строку-паттерн в /var/lib/mysql, где лежит и этот logfile, и другие файлы mariadb.
Действительно, есть и таблица, и ещё какой-то дополнительный файл. Что там прочитал grep, можно проверить с помощью Natch:
Особенность Natch в том, что потоки данных отслеживаются в первую очередь внутри программ и между ними. Данные на диске же напрямую пока не отслеживаются, поэтому если они записались не через системный вызов write, а например, через fsync, то стрелочка на диаграмме не появится. Тем не менее, исследовать работу СУБД всё же получается, хоть и применяя вспомогательные утилиты.
Теперь попробуем удалить данные из БД с помощью команды DELETE. После удаления, останавливаем сервис СУБД и делаем grep. Поток данных из ib_logfile0 в grep не обнаружился, значит MariaDB умеет делать так, чтобы ОС не кэшировала данные этого файла в памяти.
Так как Natch не видит grep на графе, посмотрим логи операций с файлами.
Удивительно, что данные из таблицы пропали после обычного DELETE. Теперь попробуем удалить всю БД с помощью команды DROP DATABASE.
Получилось всё то же самое. Файлов с таблицами уже нет на диске, а данные в ib_logfile0 остались. Всё, как и в остальных СУБД, "чувствительные" данные хоть где-то, да сохраняются. Так что у MariaDB отличается только стратегия работы с файлами таблиц.
Заключение
Оказывается, сертифицированные и enterprise-версии открытых продуктов существуют не просто так. Ни одна из проверенных свободных СУБД не позволяет вычистить удаляемые данные изо всех файлов. Поэтому, если вы строите сертифицируемую систему, просто так взять и установить СУБД из какого-нибудь debian-репозитория не выйдет.
Ссылки
Комментарии (24)
michaelkuz
06.12.2024 05:40А потом сертифицированная система управления базой данных должна дать команду сертифицированной системе бакапов, для сертифицированного удаления сертифицированных архивных копий где могут находиться данные требующие сертифицированного удаления
Иначе даже сертифицированная база данных не сможет надежно удалить данные. Их всегда можно поднять из бакапа
shurutov
06.12.2024 05:40Вот-вот. Удалили секретные данные из отовсюду, и при внезапном аудите поехала бухгалтерская отчётность. Умиляют меня вот эти вот требования.
mayorovp
06.12.2024 05:40Появление данных в новом файле WAL при исследовании Postgres скорее всего произошло из-за переиспользования этих файлов. Пытались ли вы прописать постгре команду "архивации", которая бы затирала "архивируемый" сегмент?
Dovgaluk Автор
06.12.2024 05:40Пробовали. Насколько я понимаю, удаляются те сегменты (файлы) WAL, которые уже не нужны. А у нас был только один.
VVitaly
06.12.2024 05:40:-) Вы еще не пробовали искать "удаленные" секретные данные на "сырых" носителях... А не в файловой системе... :-) Вот где будет "много сюрпризов".... А на "уровне виртуализации" эти "приключения" еще "интереснее"....
LesnoyChelovek
06.12.2024 05:40Да, всё так, но справедливо только для опенсорсных систем. В основных энтерпрайзных СУБД эта проблема решена с помощью специальных утилит.
Например, В Postgres Pro есть механизмы, которые позволяют удалять данные безвозвратно, заполняя нулями освобожденные СУБД файлы. Вообще-то это могут делать и соответствующие утилиты ОС; но вот способность базы вовремя почистить ненужные ей более версии строк, фрагменты журнальных файлов и блоки памяти уникальна.
Вот тут в деталях: https://postgrespro.ru/docs/postgrespro/17/memory-purge-cert
supercat1337
06.12.2024 05:40Мы используем частенько MySQL и MariaDB. А что если перед удалением апдейтить значения? Проблемы не будет?
Dovgaluk Автор
06.12.2024 05:40Скорее всего в логах транзакций будут и те, и другие. Но надо проверять, конечно.
supercat1337
06.12.2024 05:40А так, вообще идея была такая: создаём в таблице столбец is_deleted. При фейковом удалении ставим 1. А когда захотим по-настоящему удалить данные, то чувствительные столбцы апдейтим нуллами или иными пустышками.
Буду очень рад, если посмотрите через свою систему. Важный вопрос вы подняли.
Dovgaluk Автор
06.12.2024 05:40Ну вот я попробовал. Добавил те же данные, а потом затёр их. В WAL все сохранилось.
Скриншот
supercat1337
06.12.2024 05:40Ага, спасибо. Вы на постгресе попробовали. Что касается MariaDB и MySQL, можно сделать так:
mysql> PURGE BINARY LOGS BEFORE NOW();
Но в интернетах пишут, что есть нюанс.
В /etc/my.cnf нужно включить log-bin=mysql-bin, иначе эффекта не будет.
Если потом опять отключить эту опцию и перезапустить сервер, то логи создаваться уже не будут.
YegorP
06.12.2024 05:40Сохраняйте в зашифрованном виде. При необходимости удалить просто "выкидывайте" ключ для расшифровки вместе с удалением данных. Тогда задача хотя бы сводится к надёжному хранению и удалению ключей.
sshikov
06.12.2024 05:40Разве ключ для расшифровки у вас свой для каждой [удаляемой] строки? Скорее всего нет, поэтому задача к этому не сводится. Я могу себе условно представить один ключ на таблицу, и выкинуть его при удалении строк ни никак не получится.
Идея-то насчет шифрования хорошая, но похоже непрактичная.
vvm13xx
06.12.2024 05:40Шифрование не просто хорошая, а обязательная вещь. И раз пошла такая пьянка/паранойя, можно перешифровывать имеющиеся записи. В простейшем виде так: когда удаляем N строк, перед этим создаём новый ключ, неудаляемые записи перешифровываем с ним, удаляемые забиваем нулями. Затем убиваем старый ключ. Затем выполняем (сброс буферов|checkpoint) и переключение на новый WAL/redo log/etc. Осталось сделать бекап (базы|табличного пространства|таблицы) и надёжно затереть архивный (WAL|транзакционный лог|etc).
YegorP
06.12.2024 05:40Не вижу принципиальных проблем держать свой ключ на каждую строку или перешифровывать всю колонку единым ключом кроме удаляемой ячейки (можно оптимизировать пакетированием - делать раз в сутки, например). Практичность будет определяться требованиями к надёжному удалению в том числе.
sshikov
06.12.2024 05:40По-моему почти все (ну или многие) практически существующие (для постгреса, для определенности) системы шифрования предполагают, что ключ у клиента. Просто потому, что одно из применений шифрования - это защита данных от админа в том числе. Я бы посмотрел, как вы будете распространять клиентам (особенно если их число заранее неизвестно) вновь выпускаемые ключи, и управлять удаляемыми ключами для удаленных строк.
Ну т.е. я не хочу сказать, что это сделать невозможно, но это далеко нетривиальная задача, которую, как мне кажется, никто реально не решил, и готового такого решения на рынке нет. Я могу ошибаться разумеется, потому что исхожу просто из документации на постгрес, где предлагается пяток примерно решений для шифрования, ни одно из которых под ваше описание не подходит. Если по-простому - то везде один ключ на базу. Даже до ключа на таблицу никто не доходит (хотя по-хорошему, как раз ролевая модель с правами на уровне таблиц, она достаточно типична, и в ней ключ на таблицу был бы логичен).
YegorP
06.12.2024 05:40Вот, нашёл заметку о cryptographic erasure, где пишут, что большие корпорации уже используют такой подход: https://www.techtarget.com/searchcloudcomputing/feature/How-Azure-AWS-Google-handle-data-destruction-in-the-cloud
sshikov
06.12.2024 05:40Заметьте, что там написано про tenant encryption key, т.е. это конечно разговор про шифрование и пр. - но скорее применительно к облакам, где вы можете случайно (ну или неслучайно) получить доступ к чужим данным, когда в облаке много разных компаний. Т.е. речь о защите скорее базы целиком, нежели отдельных записей. Это вполне реальный случай, и к нам наши безопасники регулярно с такими вопросами приходят - но это другой случай.
Но я быстро прочитал, если вы там видите что-то другое - ткните пальцем, это правда интересно.
mayorovp
06.12.2024 05:40Фундаментальная проблема отдельного ключа на каждую строку - в том, что к хранению ключей предъявляются те же самые требования, что и к хранению шифруемых данных. Иными словами, для хранения ключей нужна сертифицированная СУБД с надёжным удалением. Но если такая СУБД есть - зачем вообще трястись над ключами, если можно хранить в ней сразу необходимые данные?
vadimr
В задачи СУБД общего назначения и не входит очистка дисков от данных. Это достаточно ресурсоёмкая операция, и было бы удивительно, если бы она выполнялась просто так, без предъявленных требований по защите информации.
А инструмент, конечно, интересный.