"Bezpieczeństwo bazy danych jest jak dobre hasło: możesz wymyślić najbardziej skomplikowany klucz, ale jeśli zapiszesz go na karteczce i przykleisz do monitora — to nie ma żadnego sensu." Dlatego naszym zadaniem jest nie tylko ustawić mechanizmy ochrony, ale też unikać typowych błędów, które mogą zniweczyć cały wysiłek.
1. Używanie ról z nadmiernymi uprawnieniami
Często programiści boją się ograniczać dostęp i tworzą role z szerokimi uprawnieniami, na przykład dając prawa SUPERUSER albo ALL PRIVILEGES. Ich argument brzmi: "No bo może się przyda!". Ale role z nadmiernymi uprawnieniami to ogromna dziura w bezpieczeństwie.
Przykład nadmiernych uprawnień:
GRANT ALL PRIVILEGES ON DATABASE university TO student_role;
W tym przypadku student_role dostaje pełny dostęp do wszystkich danych w bazie. Nawet jeśli rola miała tylko czytać dane, teraz może kasować tabele, zmieniać strukturę i nawet odebrać dostęp administratorowi.
Jak tego uniknąć?
Twórz role z minimalnym zestawem uprawnień. To się nazywa zasada minimalnych przywilejów. Na przykład rola do czytania danych powinna wyglądać tak:GRANT CONNECT ON DATABASE university TO student_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO student_role;
To podejście pozwala jasno określić, co rola student_role może robić: łączyć się z bazą i tylko czytać dane.
2. Brak szyfrowania poufnych danych
Wyobraź sobie tabelę users, gdzie trzymamy hasła jako zwykły tekst:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
password TEXT NOT NULL
);
Jeśli ktoś niepowołany dostanie się do tej tabeli, ma hasła wszystkich użytkowników. To jak trzymanie kluczy do mieszkania pod wycieraczką.
Żeby tego uniknąć, używaj szyfrowania haseł przez pgcrypto. Na przykład:
CREATE EXTENSION IF NOT EXISTS pgcrypto;
INSERT INTO users (username, password)
VALUES ('johndoe', pgp_sym_encrypt('secure_password', 'encryption_key'));
Do sprawdzania hasła możesz użyć deszyfrowania:
SELECT username
FROM users
WHERE pgp_sym_decrypt(password::BYTEA, 'encryption_key') = 'secure_password';
Nigdy nie trzymaj poufnych informacji w otwartym tekście!
3. Ignorowanie SQL injection
SQL injection to wciąż jeden z najpopularniejszych sposobów ataku, bo programiści dalej budują zapytania przez interpolację tekstu. Oto przykład:
DO $$
DECLARE
username TEXT := 'John';
query TEXT;
BEGIN
query := 'SELECT * FROM users WHERE username = ''' || username || ''';';
EXECUTE query;
END $$;
Jeśli ktoś zamiast nazwy użytkownika poda John' OR '1'='1, efektem będzie wyciek wszystkich danych z tabeli users.
Jak tego uniknąć? Używaj zapytań z parametrami:
PREPARE user_query (TEXT) AS
SELECT * FROM users WHERE username = $1;
EXECUTE user_query('John');
Tutaj zmienna jest podstawiana bezpiecznie, więc nie ma szans na injection.
4. Zła konfiguracja pg_hba.conf
pg_hba.conf — to główne narzędzie kontroli dostępu po adresach IP. Błędy w konfiguracji mogą sprawić, że dostęp będzie otwarty szerzej niż powinien.
Przykład złej konfiguracji:
host all all 0.0.0.0/0 trust
Ten wpis pozwala każdemu użytkownikowi połączyć się z dowolną bazą z dowolnego adresu IP bez hasła.
Jak tego uniknąć? Ustawiaj dostęp tylko dla konkretnych adresów IP i używaj metody uwierzytelniania md5 albo scram-sha-256:
host university student_role 192.168.1.0/24 md5
To ogranicza dostęp dla student_role tylko z sieci lokalnej i wymaga hasła.
Po zmianach w pg_hba.conf nie zapomnij zastosować ich komendą:
pg_ctl reload
5. Złe użycie ROW LEVEL SECURITY
RLS to potężne narzędzie, ale jest bezużyteczne, jeśli źle je ustawisz albo zapomnisz włączyć. Na przykład, nawet po napisaniu polityki dostępu, ona nie zadziała, jeśli RLS jest wyłączony:
CREATE POLICY my_policy ON users
USING (username = current_user);
-- Ale RLS nie jest włączony!
SELECT * FROM users; -- Dostaniesz wszystkie wiersze!
Jak tego uniknąć? Nie zapomnij włączyć RLS:
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
I sprawdzaj, jak polityka działa:
SET ROLE student_role;
SELECT * FROM users; -- Widzisz tylko wiersze zgodne z polityką.
6. Nieprzemyślane działania administratorów
Czasem administratorzy bazy mają pełny dostęp do wszystkich danych, nawet jeśli nie jest im to potrzebne do pracy. To dodatkowe ryzyko wycieku, jeśli konto admina zostanie przejęte.
Jak tego uniknąć? Stosuj podział ról. Do zadań administracyjnych stwórz osobną rolę bez dostępu do danych:
CREATE ROLE admin_role WITH LOGIN CREATEDB CREATEROLE;
Do dostępu do danych stwórz inną rolę z minimalnymi uprawnieniami:
CREATE ROLE data_analyst_role;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO data_analyst_role;
Przypisz role użytkownikom w zależności od ich zadań:
GRANT admin_role TO some_user;
GRANT data_analyst_role TO another_user;
7. Niewystarczające logowanie
Jeśli nie ustawisz logowania, nie dowiesz się o podejrzanych akcjach, dopóki nie będzie za późno.
Przykład braku logowania:
-- Brak ustawień w pliku postgresql.conf
log_statement = 'none';
Jak tego uniknąć? Włącz chociaż podstawowe logowanie:
log_statement = 'all'
log_connections = on
log_disconnections = on
Dzięki temu zobaczysz wszystkie wykonane zapytania, połączenia i rozłączenia.
Możesz też ustawić audyt przez rozszerzenie pgAudit dla większej kontroli:
CREATE EXTENSION pgaudit;
8. Używanie przestarzałych metod uwierzytelniania
Stare metody uwierzytelniania, jak password, nie dają wystarczającej ochrony.
Jak tego uniknąć? Przełącz się na bezpieczniejsze metody, np. scram-sha-256:
ALTER SYSTEM SET password_encryption = 'scram-sha-256';
I zaktualizuj hasła użytkowników:
ALTER USER student_role WITH PASSWORD 'new_secure_password';
Te problemy mogą wydawać się drobne, ale każdy z nich może zamienić się w poważną lukę bezpieczeństwa. Twoim zadaniem jest prowadzić bazę tak, jakby każdy użytkownik próbujący się połączyć był podejrzany. Jak to mówią, "ufaj, ale sprawdzaj". Teraz masz narzędzia nie tylko do ustawienia bezpieczeństwa, ale też do unikania najczęstszych błędów. Łap szczęście za ogon i niech twoje dane będą bezpieczne!
GO TO FULL VERSION