"Безпека бази даних схожа на крутий пароль: ти можеш вигадати найскладніший ключ, але якщо напишеш його на стікері й приліпиш на монітор — сенсу нуль." Тому наше завдання — навчитися не лише налаштовувати захист, а й уникати типових помилок, які можуть звести нанівець усі зусилля.
1. Використання ролей із надмірними правами
Часто розробники бояться обмежити доступ і створюють ролі з широкими привілеями, наприклад, даючи права SUPERUSER або ALL PRIVILEGES. Їхній аргумент звучить так: "А раптом знадобиться!". Але ролі з надмірними правами — це величезна діра в безпеці.
Приклад надмірних прав:
GRANT ALL PRIVILEGES ON DATABASE university TO student_role;
У цьому випадку student_role отримує повний доступ до всіх даних у базі. Навіть якщо роль мала лише читати дані, тепер вона може видаляти таблиці, змінювати структуру і навіть забрати доступ у адміністратора.
Як уникнути?
Створюй ролі з мінімальним набором прав. Це називається принцип мінімальних привілеїв. Наприклад, роль для читання даних має виглядати так:GRANT CONNECT ON DATABASE university TO student_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO student_role;
Такий підхід чітко визначає, що роль student_role може робити: підключатися до бази й лише читати дані.
2. Відсутність шифрування конфіденційних даних
Уяви таблицю users, де ми зберігаємо паролі у вигляді звичайного тексту:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL
);
Якщо зловмисник отримає доступ до цієї таблиці, він отримає паролі всіх користувачів. Це як зберігати ключі від квартири під килимком.
Щоб цього не сталося, використовуй шифрування паролів через pgcrypto. Наприклад:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
INSERT INTO users (username, password)
VALUES ('johndoe', pgp_sym_encrypt('secure_password', 'encryption_key'));
Для перевірки пароля можна використати дешифрування:
SELECT username
FROM users
WHERE pgp_sym_decrypt(password::BYTEA, 'encryption_key') = 'secure_password';
Ніколи не зберігай конфіденційну інформацію у відкритому вигляді!
3. Ігнорування SQL-інʼєкцій
SQL-інʼєкції залишаються одним із найпопулярніших способів атак, і причина в тому, що розробники досі будують запити через стрічкову інтерполяцію. Ось приклад:
DO $$
DECLARE
username TEXT := 'John';
query TEXT;
BEGIN
query := 'SELECT * FROM users WHERE username = ''' || username || ''';';
EXECUTE query;
END $$;
Якщо зловмисник замість імені користувача відправить John' OR '1'='1, результат — витік усіх даних із таблиці users.
Як уникнути? Використовуй параметризовані запити:
PREPARE user_query (TEXT) AS
SELECT * FROM users WHERE username = $1;
EXECUTE user_query('John');
Тут змінна підставляється безпечно, і інʼєкція не спрацює.
4. Неправильне налаштування pg_hba.conf
pg_hba.conf — це головний інструмент контролю доступу за IP-адресами. Помилки в його налаштуванні можуть призвести до того, що доступ буде відкритий ширше, ніж потрібно.
Приклад поганої конфігурації:
host all all 0.0.0.0/0 trust
Цей рядок дозволяє будь-якому користувачу підключатися до будь-якої бази з будь-якої IP-адреси без пароля.
Як уникнути? Налаштовуй доступ лише для конкретних IP-адрес і використовуй метод автентифікації md5 або scram-sha-256:
host university student_role 192.168.1.0/24 md5
Це обмежує доступ для student_role лише з локальної мережі з паролем.
Після змін у pg_hba.conf не забудь застосувати їх командою:
pg_ctl reload
5. Неправильне використання ROW LEVEL SECURITY
RLS — потужний інструмент, але він марний, якщо його неправильно налаштувати або забути увімкнути. Наприклад, навіть після написання політики доступу вона не працюватиме, якщо RLS вимкнений:
CREATE POLICY my_policy ON users
USING (username = current_user);
-- Але RLS не увімкнено!
SELECT * FROM users; -- Отримаємо всі рядки!
Як уникнути? Не забудь увімкнути RLS:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
І перевіряй, як політика застосовується:
SET ROLE student_role;
SELECT * FROM users; -- Видно лише рядки, що підходять під політику.
6. Не враховані дії адміністраторів
Іноді адміністратори бази даних мають повний доступ до всіх даних, навіть якщо це не потрібно для їхньої роботи. Це створює додатковий ризик витоку, якщо обліковий запис адміністратора скомпрометовано.
Як уникнути? Використовуй розділення ролей. Для адміністрування створи окрему роль без доступу до даних:
CREATE ROLE admin_role WITH LOGIN CREATEDB CREATEROLE;
Для доступу до даних створи іншу роль з мінімальними правами:
CREATE ROLE data_analyst_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO data_analyst_role;
Призначай ролі користувачу залежно від його задач:
GRANT admin_role TO some_user;
GRANT data_analyst_role TO another_user;
7. Недостатнє логування
Якщо не налаштувати логування, ти не дізнаєшся про підозрілі дії, поки не стане запізно.
Приклад відсутності логування:
-- Жодних налаштувань у файлі postgresql.conf
log_statement = 'none';
Як уникнути? Увімкни хоча б базове логування:
log_statement = 'all'
log_connections = on
log_disconnections = on
Це дозволить бачити всі виконані запити, підключення та відключення.
Можеш також налаштувати аудит через розширення pgAudit для більш детального контролю:
CREATE EXTENSION pgaudit;
8. Використання застарілих методів автентифікації
Використання застарілих методів автентифікації, таких як password, не дає достатнього захисту.
Як уникнути? Перейди на більш безпечні методи, наприклад scram-sha-256:
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
І онови паролі користувачів:
ALTER USER student_role WITH PASSWORD 'new_secure_password';
Ці проблеми можуть здатися дрібницями, але кожна з них може перетворитися на серйозну діру в безпеці. Твоє завдання — вести базу так, ніби кожен, хто намагається підключитися, підозрілий. Як кажуть, "довіряй, але перевіряй". Тепер у тебе є інструменти не лише для налаштування безпеки, а й для уникнення найпоширеніших помилок. Лови удачу за хвіст, і нехай твої дані будуть у безпеці!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ