\df *crypt
en psql revela los tipos de argumentos de pgcrypto encrypt
y decrypt
funciones ( como lo hacen los documentos de 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
...
entonces las funciones encrypt
y decrypt
esperan que la clave sea bytea
. Según el mensaje de error, "es posible que deba agregar conversiones de tipo explícito".
Sin embargo, funciona bien aquí en la página 9.1, por lo que sospecho que hay más de lo que has mostrado. ¿Quizás tenga otra función también nombrada encrypt
con tres argumentos?
Así es como funciona en una Pg 9.1 limpia:
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! ¡Riesgo clave de exposición, se requiere precaución extrema del administrador!
Por cierto, piense detenidamente si PgCrypto es realmente la opción correcta. Las claves en sus consultas se pueden revelar pg_stat_activity
y el sistema registra a través de log_statement
o mediante criptodeclaraciones que fallan con un error. En mi opinión, con frecuencia es mejor hacer criptografía en la aplicación .
Sea testigo de esta sesión, con client_min_messages
habilitado para que pueda ver lo que aparecería en los registros:
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)
Vaya, clave posiblemente expuesta en los registros si log_min_messages
es lo suficientemente baja. Ahora está en el almacenamiento del servidor, junto con los datos cifrados. Fallar. Mismo problema sin log_statement
que ocurra un error que haga que la declaración se registre, o posiblemente si auto_explain
está habilitada.
La exposición a través de pg_stat_activity
también es posible. Abra dos sesiones y:
- 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();
Whoops! Ahí va la llave otra vez. Puede ser reproducido sin LOCK TABLE
un atacante sin privilegios, es más difícil cronometrarlo correctamente. El ataque a través de pg_stat_activity
puede evitarse revocando el acceso pg_stat_activity
desde public
, pero solo demuestra que podría no ser mejor enviar su clave a la base de datos a menos que sepa que su aplicación es lo único que accede a ella. Incluso entonces, no me gusta.
Si se trata de contraseñas, ¿debería guardarlas?
Además, si está almacenando contraseñas, no las cifre en ambos sentidos; si es posible usar contraseñas salinas, entonces hágalas hash y almacene el resultado . Por lo general, no necesita poder recuperar el texto en claro de la contraseña, solo confirme que el hash almacenado coincida con la contraseña que el usuario le envía para iniciar sesión cuando está en hash con la misma sal.
Si es auténtico, deja que alguien más lo haga por ti
Aún mejor, no almacene la contraseña, autentíquese con LDAP, SASL, Active Directory, un proveedor de OAuth u OpenID, o algún otro sistema externo que ya esté diseñado y funcionando.
Recursos
y mucho mas