Come usare la crittografia aes in PostgreSQL?


15

Ho provato la crittografia aes usando la seguente dichiarazione:

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

che ha funzionato, ma non sono in grado di decrittografare il valore. L'ho inserito in un campo di tipo di dati bytea ma non sono sicuro che fosse il modo giusto.

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

mi dà l'errore

ERRORE: la funzione decrypt (bytea, unknown, unknown) non esiste
LINEA 1: SELECT decrypt (pw, 'key', 'aes') DA tabelle WHERE ID = 7; ^
SUGGERIMENTO: nessuna funzione corrisponde al nome e ai tipi di argomento indicati. Potrebbe essere necessario aggiungere cast di tipo esplicito.

Significa davvero che encrypt () è una funzione esistente, ma non decrypt ()? In quale altro modo è possibile recuperare valori crittografati con aes?

Risposte:


16

\df *cryptin psql rivela i tipi di argomenti di pgcrypto encrypte decryptfunzioni (così come i documenti 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
 ...

quindi entrambe le funzioni encrypte si decryptaspettano che la chiave sia bytea. Come da messaggio di errore, "potrebbe essere necessario aggiungere cast di tipo esplicito".

Tuttavia, funziona bene qui a Pg 9.1, quindi sospetto che ci sia molto di più di quanto tu abbia mostrato. Forse hai un'altra funzione chiamata anche encryptcon tre argomenti?

Ecco come funziona su una Pg 9.1 pulita:

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! Rischio chiave di esposizione, è richiesta estrema cautela da parte dell'amministratore!

A proposito, ti preghiamo di pensare attentamente se PgCrypto è davvero la scelta giusta. Le chiavi delle query possono essere rivelate pg_stat_activitye il sistema accede tramite log_statemento tramite istruzioni crittografiche che non riescono con un errore. IMO è spesso meglio fare criptovalute nell'applicazione .

Prova questa sessione, con client_min_messagesabilitato in modo da poter vedere cosa apparirebbe nei registri:

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)

Whoops, chiave eventualmente esposta nei registri se log_min_messagesè abbastanza bassa. Ora è nella memoria del server, insieme ai dati crittografati. Fallire. Stesso problema senza log_statementche si verifichi un errore che causi il log dell'istruzione, o possibilmente se auto_explainabilitato.

pg_stat_activityÈ anche possibile l' esposizione tramite . Aprire due sessioni e:

  • 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();

Ops! Ecco di nuovo la chiave. Può essere riprodotto senza il LOCK TABLEda un attaccante senza privilegi, è solo più difficile cronometrarlo nel modo giusto. L'attacco tramite pg_stat_activitypuò essere evitato revocando l'accesso a pg_stat_activityda public, ma ciò dimostra che potrebbe non essere meglio inviare la chiave al DB a meno che tu non sappia che la tua app è l'unica cosa ad accedervi. Anche allora, non mi piace.

Se si tratta di password, dovresti memorizzarle?

Inoltre, se stai memorizzando le password, non crittografarle in due modi; se possibile tutte le password salt, quindi cancellale e memorizza il risultato . Di solito non è necessario essere in grado di recuperare il testo in chiaro della password, confermare solo che l'hash memorizzato corrisponde alla password che l'utente ti invia per accedere quando è hash con lo stesso salt.

Se è auth, lascia che qualcun altro lo faccia per te

Ancora meglio, non archiviare affatto la password, eseguire l'autenticazione con LDAP, SASL, Active Directory, un provider OAuth o OpenID o qualche altro sistema esterno già progettato e funzionante.

risorse

e molto altro.


Non è più di quello che ho mostrato, e non ho definito nuove funzioni, è un nuovo postgresql installato. È abbastanza irritante che anche il tuo campione e la prima dichiarazione selezionata che ho pubblicato non funzionino, restituendo lo stesso errore pubblicato sopra. Da qualche parte qualcosa è andato storto ... grazie comunque per la tua risposta.
32bitfloat,

Prova su un nuovo CREATEdatabase d da template0; es. CREATE DATABASE testdb TEMPLATE template0quindi CREATE EXTENSION pgcrypto;e prova. Vedi se c'è qualcosa di complicato in template1.
Craig Ringer,

Solo una nota relativa alla decodifica bidirezionale nel db. Non penso che sia sempre nella direzione sbagliata, ma aggiunge complessità e dovunque tu ti occupi di questo devi davvero occuparti della gestione delle chiavi che può essere più complicata nel db.
Chris Travers,

Inoltre, al 100% secondo l'idea che non dovresti MAI dover decrittografare le password e che l'aggancio a un sistema gestito da più persone è di solito una vittoria significativa dal punto di vista della sicurezza.
Chris Travers,

3
lol, +1 per "Awooga! Awooga!"
Jeromy francese
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.