Informacje trzymane w bazie danych często mają mega dużą wartość dla firmy. Niestety, są też łakomym kąskiem dla różnych złych ludzi. Dlatego warto pomyśleć o szyfrowaniu — to jeden ze sposobów, żeby chronić dane przed niepowołanymi osobami.
Szyfrowanie pomaga zabezpieczyć poufne info: na przykład hasła, numery kart kredytowych czy dane osobowe. Dodatkowo, pomaga spełnić wymagania różnych przepisów, jak GDPR czy HIPAA. A jeśli dojdzie do wycieku, zaszyfrowane dane są dużo mniej podatne na atak, więc potencjalne szkody są mniejsze.
W PostgreSQL masz wygodne funkcje do szyfrowania symetrycznego. Dzięki pgp_sym_encrypt(data, key) możesz zaszyfrować dane, a potem odszyfrować je za pomocą pgp_sym_decrypt(encrypted_data, key), używając tego samego klucza. Prosto i bezpiecznie.
Przykład szyfrowania danych
Krok 1: Tworzenie tabeli
Stwórzmy tabelę users z kolumną, która będzie trzymać zaszyfrowane numery telefonów:
-- Tworzenie tabeli z kolumną na zaszyfrowane dane
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
phone_encrypted BYTEA -- Tu będą trzymane zaszyfrowane numery telefonów
);
Krok 2: Dodawanie danych z szyfrowaniem
Teraz dodajemy użytkownika, szyfrując jego numer telefonu:
-- Wstawianie danych z szyfrowaniem
INSERT INTO users (username, phone_encrypted)
VALUES ('john_doe', pgp_sym_encrypt('123-456-7890', 'my_secret_key'));
Zwróć uwagę na użycie funkcji pgp_sym_encrypt. my_secret_key — to nasz klucz symetryczny. W realu klucz powinien być trudny do zgadnięcia i dobrze zabezpieczony.
Krok 3: Pobieranie danych z deszyfrowaniem
Kiedy trzeba dostać się do danych, możemy je odszyfrować:
-- Pobieranie danych z deszyfrowaniem
SELECT
username,
pgp_sym_decrypt(phone_encrypted, 'my_secret_key') AS phone
FROM users;
Jeśli klucz jest poprawny, zobaczysz oryginalny numer telefonu.
Wyższy poziom: dodawanie szyfrowania do istniejącej tabeli
A co jeśli tabela już istnieje i chcemy zacząć szyfrować dane w jednej z jej kolumn? Zobaczmy przykład.
Krok 1: Tworzenie nowej kolumny
Załóżmy, że mamy tabelę customers i chcemy zaszyfrować kolumny z numerami kart kredytowych:
-- Dodawanie nowej kolumny na zaszyfrowane dane
ALTER TABLE customers ADD COLUMN card_number_encrypted BYTEA;
Krok 2: Przenoszenie danych do zaszyfrowanej kolumny
Szyfrujemy istniejące dane i wrzucamy je do nowej kolumny:
-- Szyfrowanie danych i przenoszenie do nowej kolumny
UPDATE customers
SET card_number_encrypted = pgp_sym_encrypt(card_number, 'my_other_secret_key');
Krok 3: Usuwanie niezaszyfrowanej kolumny
Po udanym szyfrowaniu starej kolumny można się pozbyć:
-- Usuwanie starej niezaszyfrowanej kolumny
ALTER TABLE customers DROP COLUMN card_number;
Teraz dane są chronione szyfrowaniem i dostęp do nich jest możliwy tylko z kluczem.
Specyfika pracy z zaszyfrowanymi danymi
Jest kilka ważnych rzeczy przy pracy z zaszyfrowanymi kolumnami:
Typ danych:
- Zaszyfrowane wartości są trzymane w formacie binarnym (
BYTEA), a nie jako czytelny tekst. - Przy zapytaniach trzeba używać funkcji deszyfrujących.
Wyszukiwanie i filtrowanie:
- Nie da się bezpośrednio szukać po zaszyfrowanych danych, np.:
SELECT * FROM users WHERE phone_encrypted = '123-456-7890'; -- NIE ZADZIAŁA!
- Zamiast tego można odszyfrować dane w zapytaniu:
SELECT *
FROM users
WHERE pgp_sym_decrypt(phone_encrypted, 'my_secret_key') = '123-456-7890';
Wydajność:
Szyfrowanie i deszyfrowanie danych może spowalniać zapytania. Używaj tego tylko tam, gdzie naprawdę trzeba.
Prawdziwy scenariusz: ochrona haseł
Przechowywanie haseł to jeden z najczęstszych przypadków użycia szyfrowania. Zamiast trzymać hasła w otwartym tekście (zły pomysł), trzeba je hashować.
Hashowanie hasła z użyciem pgcrypto
Użyjemy funkcji crypt() do bezpiecznego hashowania haseł:
-- Hashowanie hasła przy wstawianiu rekordu
INSERT INTO users (username, phone_encrypted)
VALUES ('alice', crypt('moje_bezpieczne_haslo', gen_salt('bf')));
Tutaj gen_salt('bf') generuje sól do hashowania hasła.
Żeby sprawdzić hasło, porównujemy jego hash:
-- Porównanie zhashowanego hasła
SELECT username
FROM users
WHERE crypt('moje_bezpieczne_haslo', phone_encrypted) = phone_encrypted;
Porady dotyczące bezpieczeństwa
- Trzymaj klucze osobno:
Nigdy nie zapisuj kluczy symetrycznych w tej samej bazie co zaszyfrowane dane.
- Używaj trudnych kluczy:
Proste klucze, jak "123", łatwo zgadnąć.
- Regularnie zmieniaj klucze:
Żeby zapobiec wyciekom danych, warto co jakiś czas zmieniać klucze i przeszifrować dane.
GO TO FULL VERSION