Jak korzystać z szyfrowania AES w PostgreSQL?

15

Próbowałem szyfrowania AES przy użyciu następującej instrukcji:

SELECT encrypt('test', 'key', 'aes');

które działało, ale nie jestem w stanie odszyfrować wartości. Umieściłem go w polu bajtu typu danych, ale nie jestem pewien, czy to był właściwy sposób.

SELECT decrypt(pw, 'key', 'aes') FROM table WHERE ID = 1;

daje mi błąd

BŁĄD: funkcja deszyfrowania (bajt, nieznany, nieznany) nie istnieje.
LINIA 1: WYBIERZ deszyfrowanie (pw, „klucz”, „aes”) Z tabelle GDZIE ID = 7; ^
WSKAZÓWKA: Żadna funkcja nie pasuje do podanych nazw i typów argumentów. Może być konieczne dodanie rzutowania typu jawnego.

Czy to naprawdę oznacza, że ​​encrypt () jest istniejącą funkcją, ale nie deszyfruje ()? Jak inaczej mogę odzyskać zaszyfrowane wartości AES?

32bitfloat
źródło

Odpowiedzi:

16

\df *cryptw psql ujawnia typy argumentów pgcrypto encrypti decryptfunkcji ( podobnie jak dokumenty PgCrypto ):

                                List of functions
 Schema |      Name       | Result data type |   Argument data types    |  Type  
--------+-----------------+------------------+--------------------------+--------
 ...
 public | decrypt         | bytea            | bytea, bytea, text       | normal
 public | encrypt         | bytea            | bytea, bytea, text       | normal
 ...

więc obie funkcje encrypti decryptoczekują, że klucz będzie bytea. Zgodnie z komunikatem o błędzie „może być konieczne dodanie rzutowania typu jawnego”.

Jednak działa tutaj dobrze na stronie 9.1, więc podejrzewam, że jest w tym coś więcej niż pokazałeś. Być może masz inną funkcję o nazwie również encryptz trzema argumentami?

Oto jak działa na czystym Pg 9.1:

regress=# create table demo(pw bytea);
CREATE TABLE
regress=# insert into demo(pw) values ( encrypt( 'data', 'key', 'aes') );
INSERT 0 1
regress=# select decrypt(pw, 'key', 'aes') FROM demo;
  decrypt   
------------
 \x64617461
(1 row)

regress=# select convert_from(decrypt(pw, 'key', 'aes'), 'utf-8') FROM demo;
 convert_from 
--------------
 data
(1 row)

Awooga! Awooga! Ryzyko narażenia na klucz, wymagana jest szczególna ostrożność administratora!

BTW, proszę dokładnie przemyśleć, czy PgCrypto jest naprawdę właściwym wyborem. Klucze w twoich zapytaniach mogą zostać ujawnione, pg_stat_activitya system loguje się za pomocą log_statementlub za pomocą instrukcji kryptograficznych, które kończą się błędem. IMO często lepiej jest zrobić kryptografię w aplikacji .

Obserwuj tę sesję z client_min_messageswłączoną funkcją, aby zobaczyć, co pojawi się w dziennikach:

regress# SET client_min_messages = 'DEBUG'; SET log_statement = 'all'; 
regress=# select decrypt(pw, 'key', 'aes') from demo;
LOG:  statement: select decrypt(pw, 'key', 'aes') from demo;
LOG:  duration: 0.710 ms
  decrypt   
------------
 \x64617461
(1 row)

Ups, klucz może być ujawniony w logach, jeśli log_min_messagesjest wystarczająco niski. Jest teraz w pamięci serwera wraz z zaszyfrowanymi danymi. Zawieść. Ten sam problem bez log_statementwystąpienia błędu powodującego zalogowanie się instrukcji lub ewentualnie auto_explainwłączenia.

pg_stat_activityMożliwa jest także ekspozycja przez . Otwórz dwie sesje i:

  • S1: BEGIN;
  • S1: LOCK TABLE demo;
  • S2: select decrypt(pw, 'key', 'aes') from demo;
  • S1: select * from pg_stat_activity where current_query ILIKE '%decrypt%' AND procpid <> pg_backend_pid();

Ups! Znowu idzie klucz. Może zostać odtworzony bez LOCK TABLEnieuprzywilejowanego atakującego, po prostu trudniej go odpowiednio zinterpretować. Ataku poprzez pg_stat_activitymożna uniknąć, cofając dostęp dopg_stat_activity z public, ale to tylko dowodzi, że może nie być najlepiej wysłać swój klucz do DB, chyba że wiesz aplikacja jest jedyną rzeczą kiedykolwiek dostępu do niego. Nawet wtedy nie lubię.

Jeśli to hasła, czy w ogóle je przechowujesz?

Ponadto, jeśli przechowujesz hasła, nie szyfruj ich dwukierunkowo; jeśli w ogóle możliwe hasła soli, następnie je mieszaj i zapisz wynik . Zwykle nie musisz mieć możliwości odzyskania hasła w postaci tekstu jawnego, tylko potwierdź, że przechowywany skrót jest zgodny z hasłem, które użytkownik wysyła do zalogowania, gdy jest hashowane tą samą solą.

Jeśli to auth, niech ktoś zrobi to za ciebie

Co więcej, w ogóle nie przechowuj hasła, uwierzytelnij się przy użyciu LDAP, SASL, Active Directory, dostawcy OAuth lub OpenID lub innego systemu zewnętrznego, który został już zaprojektowany i działa.

Zasoby

i wiele więcej.

Craig Ringer
źródło
To nie więcej niż pokazałem i nie zdefiniowałem nowych funkcji, jest to nowy zainstalowany postgresql. Jest dość irytujące, że twoja próbka i pierwsze oświadczenie select, które opublikowałem, również nie działają w międzyczasie, zwracając ten sam błąd, co napisany powyżej. Gdzieś coś poszło nie tak ... w każdym razie dzięki za odpowiedź.
32bitfloat
Wypróbuj nową CREATEbazę danych od template0; np. CREATE DATABASE testdb TEMPLATE template0wtedy CREATE EXTENSION pgcrypto;i przetestuj. Sprawdź, czy w szablonie 1 jest coś podejrzanego.
Craig Ringer,
Tylko uwaga dotycząca dwukierunkowego deszyfrowania w bazie danych. Nie sądzę, że zawsze jest to zły kierunek, ale dodaje złożoności i gdziekolwiek sobie z tym poradzisz, naprawdę masz do czynienia z zarządzaniem kluczami, które mogą być bardziej skomplikowane w db.
Chris Travers
Również w 100% drugie zdanie, że NIGDY nie powinieneś odszyfrowywać haseł i że podłączenie się do systemu obsługiwanego przez większą liczbę osób jest zwykle znaczącą wygraną pod względem bezpieczeństwa.
Chris Travers
3
lol, +1 dla „Awooga! Awooga!”
Jeromy French