PostgreSQL
Безопасность PostgreSQL
2019. Capital One. S3 bucket с данными 100 миллионов клиентов. Доступ через EC2 с overprivileged IAM role. SQL-инъекция → AWS metadata → IAM credentials → S3 full access. Если бы PostgreSQL использовал минимальные привилегии + RLS: SQL-инъекция читает только данные текущего пользователя, не всех 100M. Security in depth - принцип минимальных привилегий спасает даже при наличии уязвимости.
- **Supabase** - RLS как центральная концепция: каждый проект обязан настроить RLS перед production. Документация показывает паттерны multi-tenant RLS для SaaS
- **Stripe** - separate roles per microservice: payments-service имеет доступ только к таблице payments, billing-service - к billing. SQL-инъекция в одном сервисе не компрометирует другие
- **AWS RDS** - принудительный SSL, параметрические группы безопасности, IAM authentication вместо паролей, automatic rotation credentials
Roles и GRANT: минимальные привилегии
**PostgreSQL использует role-based access control (RBAC).** Role = пользователь или группа. Принцип минимальных привилегий: каждое приложение имеет роль только с нужными правами. Не давать SUPERUSER и даже CREATE DATABASE без необходимости.
**Никогда не давать приложению SUPERUSER.** SUPERUSER обходит все проверки безопасности включая RLS. SQL-инъекция в приложении с SUPERUSER = полный доступ к серверу. Минимальный набор прав: CONNECT, USAGE schema, SELECT/INSERT/UPDATE/DELETE на нужные таблицы.
Приложение использует роль app_user. SQL-инъекция позволяет выполнить `DROP TABLE users`. app_user имеет только SELECT, INSERT, UPDATE, DELETE. Что произойдёт?
Row-Level Security: изоляция данных
**Row-Level Security (RLS)** - политики на уровне строк. Каждый пользователь видит только строки, проходящие политику. Прозрачно для приложения: один SELECT, PostgreSQL автоматически добавляет фильтр. Идеально для multi-tenant SaaS: одна таблица, каждый клиент видит только свои данные.
**RLS + PgBouncer**: при transaction mode server_reset_query=DISCARD ALL очищает SET app.current_user_id. Нужно устанавливать контекст в каждой транзакции или использовать session mode. Supabase решает это через JWT в заголовке запроса и специальный middleware.
RLS включён на таблице `documents`. SUPERUSER выполняет `SELECT * FROM documents`. Что происходит?
Шифрование данных: pgcrypto и прозрачное шифрование
**Шифрование данных в PostgreSQL** - несколько уровней. pgcrypto extension: шифрование отдельных столбцов в SQL. Transparent Data Encryption (TDE): шифрование файлов данных на уровне ОС. Шифрование in transit: SSL/TLS для всех соединений.
**pgcrypto шифрование в SQL** - ключ передаётся в SQL запросе, виден в pg_stat_activity и pg_log. Для production: ключи управлять через vault (HashiCorp Vault, AWS KMS), не хранить в коде. Или использовать application-level encryption до отправки в БД.
Данные зашифрованы через pgp_sym_encrypt с ключом 'hardcoded_key'. DBA получил доступ к pg_log. Что компрометирует данные?
SSL: шифрование транспорта
**SSL/TLS** шифрует данные в транзите между клиентом и PostgreSQL. Без SSL данные (включая пароли) передаются в открытом виде по сети. PostgreSQL поддерживает SSL из коробки: нужно сертификат + ключ. pg_hba.conf управляет требованиями SSL для разных клиентов.
**sslmode=prefer** (дефолт в libpq) - клиент пробует SSL, при отказе сервера переходит на нешифрованное соединение. Для production: sslmode=require или verify-full. Managed PostgreSQL (RDS, Cloud SQL, Supabase) требует SSL по умолчанию.
sslmode=prefer и сервер поддерживает SSL. Клиент подключился без SSL из-за ошибки в negotiation. Что произошло?
pg_audit: аудит действий
**pg_audit** (PostgreSQL Audit Extension) - детальное логирование SQL операций для compliance (PCI DSS, HIPAA, SOC2). Логирует какой пользователь, когда, какую команду выполнил на каком объекте. Встроенный log_statement слишком груб - pg_audit даёт структурированные записи.
**PCI DSS требует** аудита всего доступа к данным карточек (PAN, CVV). pg_audit на таблице `card_data` + централизованное хранение логов (CloudWatch, Splunk) + retention 12 месяцев = compliance без дополнительных инструментов.
Row-Level Security (RLS) и обычные GRANT привилегии - одно и то же, просто разные синтаксисы
GRANT работает на уровне объектов (таблица/схема). RLS - на уровне строк внутри таблицы. Они дополняют друг друга: GRANT = можно ли читать таблицу, RLS = какие строки видны
GRANT SELECT ON TABLE orders = читать любые строки таблицы. RLS policy = фильтр применяется к каждому запросу автоматически. Без RLS у всех пользователей с GRANT SELECT = полный доступ ко всем строкам. С RLS = видят только свои строки. Multi-tenant без RLS требует WHERE tenant_id = ? в каждом запросе - ошибка пропущенного WHERE = data leak
pgaudit.log = 'write'. Аналитик выполняет `SELECT * FROM payments`. Попадёт ли это в аудит лог?
Итоги
- **Минимальные привилегии**: приложение получает только SELECT/INSERT/UPDATE/DELETE на нужные таблицы. SUPERUSER только для DBA. Миграционная роль отдельно
- **RLS** - политики на уровне строк. Каждый видит только свои данные. Прозрачно для приложения. SUPERUSER обходит RLS
- **pgcrypto** шифрует столбцы, но ключ в SQL попадает в логи. Ключи управлять через vault, не хардкодить
- **SSL** обязателен в production: sslmode=require или verify-full. prefer = ненадёжный fallback без шифрования
- **pg_audit** для compliance (PCI DSS, HIPAA): детальный аудит DDL/DML по пользователям и объектам
Связанные темы
Безопасность пронизывает всю архитектуру PostgreSQL:
- Views и Materialized Views — security_barrier views - защита от утечки данных через оптимизацию предикатов. Альтернатива RLS для простых случаев
- Мониторинг — pg_audit + мониторинг pg_stat_activity = полная картина кто и что делает в БД. Алерты на подозрительные паттерны
- Миграции — DDL права в GRANT: миграционная роль отдельно от application role. Migrate без production SUPERUSER
Вопросы для размышления
- SaaS приложение с 10K клиентов в одной таблице `data`. Без RLS: каждый запрос должен иметь `WHERE client_id = ?`. Что произойдёт если разработчик забудет WHERE в одном запросе? Как RLS устраняет этот класс ошибок?
- Компания должна соответствовать HIPAA: аудит всего доступа к полю SSN в таблице `patients`. Как настроить pg_audit чтобы логировать только SELECT на это поле, не все SELECT на таблицу?
- При SQL-инъекции атакующий может выполнить `COPY (SELECT ...) TO PROGRAM 'curl ...'`. Какие привилегии нужны для COPY TO PROGRAM? Как правильно настроить роли чтобы это было невозможно?