
Данные требуют защиты
В базах данных хранится много информации. И обычно часть этой информации является настолько критичной, что её надо надёжно защитить от несанкционированного доступа. В рамках доступа через SQL‑интерфейс за это отвечает Access Control List (ACL). Команды GRANT, REVOKE — они работают с этим механизмом, и работают хорошо и надёжно.
Специальные возможности Postrges Pro Enterprise
В Postgres Pro Enterprise есть специальные дополнительные возможности управления доступом через ACL. Подробнее это описано в документации:
Разграничение прав между привилегированными пользователями СУБДhttps://postgrespro.ru/docs/enterprise/current/sod-separation-of-duties
Ограничение доступа администратора СУБД к даннымhttps://postgrespro.ru/docs/enterprise/current/restrict-dbms-admin-data-access
Но что делать, если злоумышленник имеет непосредственный доступ к файлам базы данных? Такое может быть, например, если это резервная копия, которая лежит в недостаточно защищённом хранилище. Или если ваша база развёрнута в облаке, где вы вообще не знаете, что за администраторы имеют физический доступ к серверам.
Да, резервные копии можно защитить, зашифровав их, современные системы резервного копирования предоставляют такие возможности. Но представьте себе, что вам надо сохранить 10 ТБ данных в архив. Даже простое копирование такого объёма занимает кучу времени, а уж если мы при этом ещё и шифруем их... Особенно болезненно идёт этот процесс с использованием криптопримитивов ГОСТ, так как для них нет аппаратной поддержки в командах процессоров Intel/AMD, ARM и даже Эльбрус. Да и время восстановления архива так же растягивается.
А если злоумышленник имеет доступ к файловой системе? Если это один из администраторов «железа», который ходит между стойками в ЦОД и обслуживает в нём серверы, в том числе баз данных? Он не имеет доступа к базе данных через SQL‑интерфейс, но видит сами файлы базы данных. Он может просто зайти в консоль на сервере и командой cp перекинуть к себе все файлы базы данных. Тут не поможет даже шифрование дисков, не то что шифрование резервной копии. А риск такого события в большом ЦОД, где роли по обслуживанию серверов поделены между разными подразделениями, хоть и низкий, но вполне вероятный.
Для таких угроз придумали следующий бастион защиты — шифрование файлов базы данных. Само собой, тут же появилось много вариантов реализации такой защиты. В силу некоторых причин юридического характера я буду в дальнейшем называть это «защитным преобразованием», или «кодированием».
Level 0. Защитное преобразование данных между клиентом и СУБД (pgprypto)
Это самый очевидный и простой в реализации способ. Вместо того чтобы хранить в БД «секретные» данные в открытом виде, их сохраняют туда уже в закодированном виде. Само собой, возникает необходимость иметь ключи для этого преобразования либо непосредственно на самом клиенте, который обращается к СУБД, либо на каком‑то proxy, который пропускает через себя запросы SQL и получаемые результаты. В мире PostgreSQL широко известно расширение pgcrypto, которое реализует такую функциональность. На его основе многие независимые вендоры разработали собственные реализации защиты.
Однако этот способ не является широко распространённым. Почему? Ответ очевиден: потому что у него много недостатков. Перечислим наиболее явные:
Необходимость распространения, синхронизации и надёжного хранения ключей доступа среди всех клиентов, обращающихся к защищённой информации в БД
каждый клиент должен иметь ключи, необходимые для декодирования защищённых таблиц, которые он читает или в которые он пишет;
эти ключи должны быть одинаковые у всех клиентов, имеющих доступ к такой таблице;
в случае компрометации или просто смены ключа — надо всё перешифровывать и распространять новые ключи среди всех клиентов.
Невозможность фильтрации и поиска средствами SQL в защищённой pgcrypto таблице
SQL executor работает с данными, непосредственно хранящимися в таблице, а они имеют нечитаемый вид.
Нельзя использовать constraints
SQL executor не может определить, что на самом деле хранится внутри строки защищённой таблицы.
Запросы к базе данных должны быть специально изменены, чтобы содержать в себе вызовы pgcrypto.
Таким образом, нам нужно что‑то, что будет «прозрачным» для SQL executor и для пользователей. И это так и называется — «прозрачное шифрование» (TDE, Transparent Data Encryption). Опять же, в силу юридических причин, я дальше буду называть его «прозрачным защитным преобразованием», или «прозрачным кодированием» (TDE, Transparent Data Encoding).
Прозрачное защитное кодирование информации
Прозрачным оно называется потому, что для всех средств доступа к информации через SQL оно выглядит так, как будто бы его и нету. Пользователь вызывает обычные SQL insert, update, select. Внутри СУБД SQL executor тоже работает с «чистыми» данными. При этом в файлах СУБД хранит нечитаемую белиберду. Красота!
В таком подходе все проблемы обращения с ключами и собственно закодированными данными решает сама СУБД. Реализовать же это можно несколькими очень сильно разными способами. Давайте посмотрим на них по порядку, по мере усложнения реализации и повышения её эффективности.
Level 1. Percona pg_tde_basic
Речь пойдёт о «классической» системе TDE от Percona, которую они как раз недавно перестали развивать: https://github.com/percona/pg_tde/
Идея простая: раз мы хотим перенести защитное кодирование с клиента на СУБД, давайте сдвинем его на этап сохранения в буфера хранения в памяти. Тогда данные в файлы таблиц (relations) и журналов (wal) будут автоматически сохраняться уже в нечитаемом виде. А самый простой способ это обеспечить — написать собственный TAM (Table Access Method), который будет как две капли воды походить на обычный heap, только кодировать записи (tuples) при сохранении в буферный кэш и декодировать при чтении оттуда. Всё, данные защищены!

Но, как говорится, «есть нюанс». Во‑первых, что там насчёт индексов? Они же не в heap / pg_tde_basic хранятся. Во‑вторых, так защитить можно только данные в heap, а если вы поставили стороннее расширение, создающее собственный TAM, например Citus? Кто защитит его данные? И главное — это работает медленно. Очень медленно.
Наши тесты показывают, что даже не в худшем сценарии, а в более‑менее оптимистичном, работа запросов замедляется серьёзнейшим образом. Если мы так защитили, скажем, словарь имён пользователей, и в ходе выполнения запросов постоянно много раз читаем одни и те же записи этого словаря из страниц буферного кэша — каждое обращение с чтением каждого имени будет требовать его декодирования. А это довольно дорогостоящая операция, даже если наш процессор поддерживает аппаратное ускорение AES, и openssl его использует.
Значит, надо переместить уровень кодирования‑декодирования на уровень ниже.
Level 2. Cybertec PGEE, Enterprise DB
Эти две реализации TDE имеют существенные отличия внутри, однако их объединяет общий архитектурный подход:

СУБД умеет работать в двух режимах: либо вообще без всякого защитного кодирования, либо как кластер Postgres, закодированный целиком, включая абсолютно все таблицы и индексы, даже системный каталог. Такой подход является самым простым в реализации: не надо думать, закодирована ли какая‑то страница или нет. Не надо думать, что происходит в WAL Buffer, всё поведение в кластере всегда одинаковое и задаётся в момент initdb. То есть главное достоинство такого подхода — простота и надёжность реализации.
По способам защитного кодирования — для этого подхода характерно использование AES‑XTS для защиты страниц relations, и AES‑CTR для защиты wal segments. CTR использует в качестве nonce/counter счётчик LSN, что обеспечивает высокую надёжность защиты. А к вопросу о надёжности защиты XTS мы вернёмся чуть позже.
Итак, достоинства этого подхода понятны. Но и недостатки его также очевидны:
Пониженная производительность. Ведь шифруется всегда весь кластер, то есть если в системе из 100 таблиц только 10 требуют защиты, то 90% ресурсов на защитное преобразование данных замедляет производительность, не принося при этом никакой пользы.
Сложность взаимодействия с технической поддержкой. PostgreSQL — очень надёжная система. Но при работе даже с такой надёжной системой иногда бывают сбои и сложные ситуации. И если для незакодированной несекретной таблицы можно достаточно быстро получить разрешение внутренней службы безопасности на передачу «битого» файла вендору техподдержки, то запросы на декодирование с целью передачи — большая головная боль.
Чтобы решить эти две проблемы, применяют подход с «пометкой» защищённых закодированных таблиц.
Level 3. Pangolin SE, Fujitsu EP, Percona Distribution
Чтобы не кодировать всё подряд, надо как‑то пометить таблицы, подлежащие защите. Производители обеих систем, Pangolin и Fujitsu, независимо пришли к идее, что самый естественный способ сделать это — выделить «защищённое» табличное пространство. Тогда все таблицы, находящиеся в таком tablespace, будут закодированы с использованием каких‑то ключей. А все прочие же — останутся незакодированными и не будут создавать дополнительной нагрузки на CPU и снижать тем самым производительность. Признаком «защищённости» в обеих системах является специальный файл, лежащий в корне файлового каталога защищённого табличного пространства. Для этого подхода также используют AES‑XTS или AES‑CBC для защиты файлов relations и AES‑CTR для защиты WAL.

Главным отличием между подходами является то, что Pangolin кодирует все wal целиком, а Fujitsu — только данные защищённых таблиц. К сожалению, подход, принятый для этого в Fujitsu, является крайне инвазивным: в самые разнообразные resource managers надо вставлять порядка 100 изменений, связанных с вызовами openssl, что создаёт проблемы при интеграции (merge) изменений из vanilla upstream. Также это делает задачу выработки уникального nonce/counter нетривиальной задачей, так как в момент XLogInsert значение надёжного счётчика, LSN, ещё неизвестно.
Реализация TDE для Pangolin была сделана несколько лет назад, и её использование на рабочих системах доказало реализуемость и надёжность этого подхода.
Также надо упомянуть новую open‑source реализацию Percona TDE, которую они опубликовали в Percona Distribution for PostgreSQL 17.4.1. Основываясь на проверенной системе управления ключами защиты от pg_tde_basic, они реализовали схему, очень похожую на Pangolin и Fujitsu. Только вместо помещения защищённых таблиц в специальный tablespace в Percona помечают их использованием особого TAM, pg_tde. Опираясь на этот признак, система работает очень похоже на то, как это сделано в Pangolin, однако есть и существенные отличия. Очевидным недостатком подхода Percona является невозможность защищать нестандартные TAM, такие как Citus Columnar и TigerData Hypercore (авторы Timescale DB). Дополнительным серьёзным недостатком текущей версии TDE в релизе 17.5 у Percona является то, что она не поддерживает работу pg_rewind, делая её фактически непригодной для использования в сложных сценариях восстановления данных. На этом же open‑source решении от Percona основана и реализация tde у Tantor.
Но хочется ещё большей безопасности
В чём же недостаток этого подхода, и почему мы в Postgres Professional пошли немного другим путём? Причин две, и они имеют один общий корень:
«нагрузка на ключ», то есть сколько гигабайтов данных можно безопасно зашифровать не меняя ключа кодирования;
повторное защитное кодирование изменённых страниц под теми же ключами.
Тут следует немного пояснить, о чём идёт речь. В криптографии считается, что одним ключом можно безопасно зашифровать лишь ограниченное количество данных. Если зашифровать слишком много, возникает риск взлома шифра через специальные атаки, основанные на статистике данных. Для кодирования WAL‑файлов способом AES‑CTR со счётчиком WAL LSN этот предел достаточно большой. Для AES‑XTS и AES‑CBC, однако, есть угроза взлома, основанная на сравнении закодированных данных за разные срезы времени. И чем больше данных имеется у взломщика в наличии, тем больше вероятность взлома.
Упомянутые выше системы реализуют различные способы снижения нагрузки на ключ, например — индивидуальный ключ защиты для отдельной таблицы и даже для отдельного relation file fork. Однако они всё равно могут оказаться недостаточными из‑за повторного кодирования одних и тех же страниц таблицы под одинаковым ключом. А ротация ключа таблицы (замена его на новый) сопряжена с полным перекодированием всей таблицы, что является медленной и блокирующей операцией.
Повторное кодирование, что тут опасного? Модель угроз, от которых мы хотим защититься — доступ злоумышленника к файлам базы данных. И доступ не однократный, а регулярный. Где злоумышленник может накапливать копии защищённых данных за разные срезы времени. После чего запускать по этим данным специальные анализаторы, которые могут значительно облегчить взлом этого защитного кодирования. Именно поэтому, хотя размер одного relation file fork ограничен 1 гигабайтом, на самом деле взломщик может увидеть в нём на порядки больше данных, копируя к себе копию этого файла в разное время.

Если погружаться в криптографию чуть глубже, истоки XTS и его недостатки хорошо описаны в статье "You Don't Want XTS". В этой же статье можно найти причины, по которым CBC считается не лучшим способом для защиты повторно модифицируемых блоков.
Итак, для повышения уровня защиты и повышения эффективности нам надо избавиться от XTS/CBC и перейти к высокоскоростным алгоритмам с аутентификацией, таким как GCM или подобным. Практически все реализации openssl поддерживают способ кодирования GCM, поэтому его можно рассматривать как основной. Также нам надо иметь «ротацию ключей», которая позволит ограничить количество информации, кодируемой с использованием одного ключа.
Все эти три алгоритма AES — XTS, CBC, GCM — оперируют блоками данных длиной 16 байт. Однако CBC является алгоритмом с обратной связью, где кодирование следующего блока зависит от результатов кодирования предыдущего, что исключает возможность его быстрого параллельного исполнения. В алгоритме XTS также кодирование следующего 16-байтного блока зависит от данных, полученных в ходе обработки предыдущего 16-байтного блока. GCM же построен таким образом, что собственно операция AES может выполняться над блоками параллельно. GCM является высоконадёжным алгоритмом, являющийся одним из 3 надёжных алгоритмов, допустимых в шифровании TLS 1.3
Level 80. Postgres Pro Enterprise
Начав разрабатывать свою реализацию TDE, мы поставили себе целью решить эти проблемы и обеспечить надёжную защиту от угрозы доступа к файлам БД:
нужна возможность менять ключ шифрования таблиц «на ходу»;
кодирование должно защищать от анализа изменений одной и той же страницы таблицы, то есть для каждого нового кодирования надо использовать уникальный IV (Initialization Vector);
целостность закодированной информации должна дополнительно защищаться при помощи MAC (Message Authentication Code).
Дополнительной целью было обеспечить возможность защищать как отдельные таблицы, так и базы данных целиком. Последнее также бывает нужно для БД с особо чувствительными данными. Давайте теперь посмотрим, как мы это реализовали.
Ротация ключей без перекодирования таблиц
В основу этого решения положена простая идея: давайте помнить, какой ключ использовался для конкретной страницы таблицы. Для этого не надо запоминать для каждой страницы ключ целиком, достаточно иметь его индекс‑идентификатор в общем списке ключей, который хранится отдельно в безопасном месте. Тогда после ротации ключей мы будем кодировать все новые данные под новым ключом, в то же время старые данные будут всё ещё доступны для нас под старыми ключами, которые использовались для их кодирования раньше. Все ключи, используемые для кодирования данных таблицы, — и старые, и новые, — хранятся в безопасном виде в общей «связке ключей».
В случае же, если надо будет полностью перекодировать всю таблицу — это можно сделать при помощи команды VACUUM FULL, которая создаст полную копию таблицы, но закодированную уже исключительно под новым ключом. Это позволяет быстро реагировать в случае компрометации ключа защиты каких‑то таблиц. Одна простая команда VACUUM FULL позволяет перекодировать эти таблицы под новым ключом, исключив возможность их взлома при помощи компрометированного ключа.
Но именно здесь начинаются сложности. Ведь если для AES‑XTS/CBC в упомянутых выше реализациях никаких специальных дополнительных данных не требовалось, то теперь нам надо где‑то запоминать идентификатор используемого для страницы ключа. Запоминать его при кодировании таблицы и доставать именно его из списка ключей для её декодирования. Нам нужно хранить где‑то «метаинформацию» по каждой странице relations. Кажется очевидным решение положить эти несколько байт прямо в страницу, и мы всерьёз рассматривали данный вариант. Однако он оказался крайне сложным в реализации, так как в коде Postgres много сейчас завязано на то, что размер special space одинаковый для всех relations данного TAM. Нам надо было бы либо проводить «обрезание» полезной области страницы абсолютно для всех TAM, включая нестандартные от Citus и Timescale DB, либо же вносить в код массу изменений, позволяющих работать с такой разносортицей. Первое — ухудшит производительность при работе с незащищёнными таблицами, второе — запредельный по сложности и рискованности подход, который отметается как бессмысленно дорогой в реализации и особенно при дальнейшей поддержке.
Решение этой проблемы мы позаимствовали из другой нашей очень успешной фичи — сжатых таблиц, CFS. По аналогии с cfm‑файлами CFS наша реализация TDE держит рядом с relation файлами специальные tde‑файлы. Только внутри она держит специнформацию не для декомпрессии, а для расшифровки страниц. Такой tde‑файл хранит 128 байт метаинформации на каждую страницу данных своего relation файла. Размер в 128 байт был взят с большим запасом, этого хватит для реализации самых разных схем защиты. Само собой при ротации ключей, так же как и при создании новых шифрованных табличных пространств, эти новые ключи автоматически передаются на standby сервера через WAL безопасным образом.
Итак, путём создания отдельного файла с метаинформацией мы успешно решили проблему хранения индекса ключа для каждой отдельной страницы. Конечно, пришлось отдельно поработать над отказоустойчивостью такого решения, когда запись изменённой страницы требует двух обращений к разным файлам, но, опираясь на наш опыт, CFS мы смогли добиться высокой надёжности.

Защита от time-based анализа
Как уже упоминалось, основная проблема XTS (не говоря уже о CBC и тем более о CTR) для защиты отдельных страниц relation файлов — это низкая устойчивость к взлому путём анализа срезов данных этой страницы за разное время. В криптографии принято считать, что хорошее шифрование даёт на выходе байтовую последовательность, неотличимую от белого шума. То есть — с абсолютной энтропией и невозможностью найти хоть какую‑то закономерность. Даже если мы несколько раз кодируем одни и те же данные, мы должны получать разный результат, неотличимый от «белого шума».
Как этого добиться? Путём генерации и использования случайных векторов инициализации (IV). Но раз мы сгенерировали и закодировали страницы relation со случайным IV, как мы потом его декодируем? Нам необходимо где‑то сохранить этот самый случайный IV. И тут нам снова на помощь приходит на tde‑файл. 128 байт было выделено с запасом, в них хватит места и на IV. Теперь при кодировании очередной страницы наша система генерирует случайный IV, защищает при помощи AES‑GCM и этого IV страницу, после чего сохраняет на диск и её саму, и соответствующую метаинформацию с IV. При декодировании происходит обратный процесс: читаем с диска страницу, читаем нужные 128 байт метаинформации, берём оттуда соответствующий IV, соответствующий идентификатор ключа и декодируем страницу.
Ещё одним важным преимуществом использования AES‑GCM является то, что на современных процессорах, поддерживающих наборы команд x86–64 AVX2 и AVX-512, этот алгоритм в новых версиях openssl работает значительно быстрее, чем AES‑CBC, и даже быстрее, чем AES‑XTS.
Теперь у нас каждый сохранённый на диск слепок страницы защищён уникальным IV, что обеспечивает безопасность от взлома путём анализа данных за разные срезы времени.
Защита от подделки при помощи MAC (Message Authentication Code)
MAC зашифрованных данных — это своего рода контрольная сумма, при помощи которой можно проверить их целостность. Openssl вычисляет его для нас в процессе кодирования, что позволяет проверить его позже при обратном декодировании. Для этого нам надо запомнить MAC для каждой закодированной страницы, что мы делаем, опять же, в tde‑файле с метаинформацией.
Обычная 16-битная контрольная сумма страниц данных Postgres даёт не такой уж и высокий уровень защиты от повреждения блока данных. И здесь MAC выступает дополнительным средством контроля целостности: 32-битный MAC даёт на несколько порядков более надёжное определение повреждения данных, нежели 16-битная контрольная сумма.
Для корректной работы стандартных утилит postgres, таких как pg_basebackup или pg_amcheck наш Postgres Enterprise отдельно рассчитывает стандартную 16-битную контрольную сумму по уже зашифрованным данным и сохраняет её на диск. Эта контрольная сумма, так же как и весь заголовок страницы, не шифруется, это позволяет стандартным утилитам, не пытающимся анализировать данные внутри страницы, корректно работать с файлами защищённых таблиц и индексов.
Защитное кодирование базы данных целиком
Благодаря тому, что наша реализация TDE не привязана к какому‑то специфическому TAM (Table Access Method), мы реализовали защитное кодирование не только отдельных таблиц, но и целых баз данных в кластере. При этом будут закодированы не только все «обычные» таблицы, созданные пользователями и приложениями, но и системный каталог. Делается это предельно понятным и прямолинейным способом — созданием базы данных в защищённом табличном пространстве.
CREATE DATABASE my_secret_database TABLESPACE my_tde_tablespace;
Можно также защитить уже существующую базу данных целиком, переместив её всю в защищённое табличное пространство:
ALTER DATABASE my_secret_database SET TABLESPACE my_tde_tablespace;
Внимание! Перемещение базы данных целиком в защищённое табличное пространство является блокирующей операцией и может занять довольно много времени!
Postgres Pro Enterprise TDE — решение проблем
Давайте теперь посмотрим, какие проблемы мы озвучивали по ходу рассмотрения других, более ранних способов защитного кодирования данных в СУБД. И ответим на вопрос, решает ли их Postgres Pro Enterprise.
Проблема: Необходимость распространения, синхронизации и надёжного хранения ключей доступа среди всех клиентов, обращающихся к защищённой информации в БД
Решена. Прозрачное защитное кодирование (TDE) использует ключи, которые безопасным образом хранятся на сервере СУБД. Для клиентов, обращающихся к защищённой информации, никаких специальных ключей кодирования не требуется.
Проблема: Невозможность фильтрации и поиска средствами SQL в защищённой pgcrypto таблице
Решена. Прозрачное защитное кодирование (TDE) позволяет механизму выполнения SQL‑запросов работать с «чистыми», некодированными данными
Проблема: Нельзя использовать constraints
Решена. Прозрачное защитное кодирование (TDE) позволяет механизму выполнения SQL‑запросов работать с «чистыми», некодированными данными
Проблема: Запросы к базе данных должны быть специально изменены, чтобы содержать в себе вызовы pgcrypto
Решена. Запросы к базе данных не требуют специальной доработки или изменений. Влияние механизма TDE на скорость выполнения запросов минимальное, в силу использования высокоскоростного алгоритма AES‑GCM.
Проблема: Ограничение нагрузки на ключ
Решена. Нагрузка на ключ кодирования таблицы можно ограничивать, используя для одной таблицы несколько разных ключей одновременно. Прозрачное кодирование данных (TDE) в Postgres Pro Enterprise устойчиво к статистическим атакам, использующим большой объём данных, кодированных под одним и тем же ключом.
Проблема: Опасность time‑based атак
Решена. Алгоритм кодирования AES‑GCM со случайным вектором инициализации (IV) устойчив к time‑based атакам.
Дальнейшее развитие Postgres Pro Enterprise TDE
Нет предела совершенству! Мы не останавливаемся на достигнутом, а продолжаем развивать нашу реализацию TDE. Основными направлениями для улучшений мы видим следующие:
CFS+TDE, сжатые и защищённые таблицы
Нас неоднократно спрашивали, можно ли защитить при помощи TDE таблицы в сжатых tablespaces. Препятствием для этого служило то, что по уровням архитектуры данные страницы сначала защищались, и лишь уровнем ниже проводилось сжатие. А так как энтропия данных после защитного преобразования крайне высокая, никакого сжатия реально не достигалось. В следующих релизах мы планируем решить эту проблему, встроив вызов защитного преобразования TDE для уже сжатых CFS данных. При этом хранением метаинформации для защитного кодирования будет управлять CFS, что позволит получать «сжато‑защищённые» табличные пространства.
Не кодировать TDE заголовки данных в WAL
Текущая реализация TDE в Postgres Pro Enterprise, так же как и большинство других реализаций, защищает данные WAL‑файлов целиком. Это привносит необходимость иметь доступ к ключам защиты для утилит типа pg_waldump и pg_probackup. То есть для просмотра структуры WAL‑файла при помощи pg_waldump, или для PITR (Point In Time Recovery) при помощи pg_probackup, нужен доступ к ключам защитного преобразования. В результате запуск pg_probackup для защищённых баз данных в remote‑режиме сопряжён с некоторыми трудностями.
Казалось бы, решение тут предельно простое: внести в заголовок WAL record признак защитного кодирования и готово! Но нет, это сопряжено с целым рядом трудностей. Если идти «путём Fujitsu», то требуется внести соответствующую логику во все resource managers, тонким слоем размазав эту логику по огромному количеству строк самого разного кода. И при этом всё равно «сторонние» resource managers от подключаемых TAM, таких как Citus Columnar и TigerData Hypercore, они всё равно окажутся незащищёнными. Дело в том, что процесс записи в WAL‑файл построен по принципу «только вперёд». Для обеспечения отказоустойчивости PostgreSQL всегда только дописывает новые данные в файл сегмента WAL, заменяя нулевые данные настоящими, и никогда не возвращается для модификации уже записанного. Причём в силу некоторых серьёзных причин контрольная сумма записи находится в её начале. Из‑за этого защитить данные WAL-записи «задним числом» невозможно.
Тем не менее, мы нашли решение этой проблемы, и в следующем большом релизе, Postgres Pro Enterprise 18, планируем его реализовать. К сожалению, внести это решение в существующий релиз Enterprise 17 невозможно, так как потребуется немного изменить формат данных в WAL‑файлах.