\df *crypt
in psql rivela i tipi di argomenti di pgcrypto encrypt
e decrypt
funzioni (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 encrypt
e si decrypt
aspettano 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 encrypt
con 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_activity
e il sistema accede tramite log_statement
o tramite istruzioni crittografiche che non riescono con un errore. IMO è spesso meglio fare criptovalute nell'applicazione .
Prova questa sessione, con client_min_messages
abilitato 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_statement
che si verifichi un errore che causi il log dell'istruzione, o possibilmente se auto_explain
abilitato.
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 TABLE
da un attaccante senza privilegi, è solo più difficile cronometrarlo nel modo giusto. L'attacco tramite pg_stat_activity
può essere evitato revocando l'accesso a pg_stat_activity
da 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.