¿Cómo usar el cifrado aes en PostgreSQL?


15

Intenté el cifrado aes usando la siguiente declaración:

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

que funcionó, pero no puedo descifrar el valor. Lo inserté en un campo de tipo de datos bytea pero no estoy seguro si esa fue la forma correcta.

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

me da el error

ERROR: la función descifrar (bytea, desconocido, desconocido) no existe
LÍNEA 1: SELECCIONAR descifrar (pw, 'clave', 'aes') DESDE la tabla DONDE ID = 7; ^
SUGERENCIA: Ninguna función coincide con el nombre dado y los tipos de argumento. Es posible que deba agregar conversiones de tipo explícito.

¿Eso realmente significa que encrypt () es una función existente, pero no descifrar ()? ¿De qué otra forma podría recuperar valores cifrados con aes?

Respuestas:


16

\df *crypten psql revela los tipos de argumentos de pgcrypto encrypty decryptfunciones ( 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 encrypty decryptesperan 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 encryptcon 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_activityy el sistema registra a través de log_statemento 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_messageshabilitado 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_messageses lo suficientemente baja. Ahora está en el almacenamiento del servidor, junto con los datos cifrados. Fallar. Mismo problema sin log_statementque ocurra un error que haga que la declaración se registre, o posiblemente si auto_explainestá habilitada.

La exposición a través de pg_stat_activitytambié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 TABLEun atacante sin privilegios, es más difícil cronometrarlo correctamente. El ataque a través de pg_stat_activitypuede evitarse revocando el acceso pg_stat_activitydesde 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


No es más de lo que mostré, y no definí nuevas funciones, es un nuevo postgresql instalado. Es bastante irritante que su muestra y la primera declaración de selección que publiqué tampoco funcionen mientras tanto, devolviendo el mismo error que se publicó anteriormente. En algún lugar algo salió mal ... gracias de todos modos por su respuesta.
32bitfloat

Pruebe en una nueva CREATEbase de datos d de template0; Por ejemplo, CREATE DATABASE testdb TEMPLATE template0entonces CREATE EXTENSION pgcrypto;y prueba. Vea si hay algo dudoso en template1.
Craig Ringer

Solo una nota sobre el descifrado bidireccional en la base de datos. No creo que siempre sea la dirección equivocada, pero agrega complejidad y en cualquier lugar que maneje esto realmente tiene que lidiar con la administración de claves, lo que puede ser más complicado en la base de datos.
Chris Travers

También 100% de segundo la noción de que NUNCA debería tener que descifrar las contraseñas y que conectarse a un sistema mantenido por más personas suele ser una ganancia significativa en términos de seguridad.
Chris Travers

3
jajaja, +1 para "¡Awooga! ¡Awooga!"
Jeromy French
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.