web-dev-qa-db-fra.com

Comment utiliser le cryptage aes dans PostgreSQL?

J'ai essayé le cryptage AES en utilisant la déclaration suivante:

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

qui a fonctionné, mais je ne suis pas en mesure de décrypter la valeur. Je l'ai inséré dans un champ de type de données bytea mais je ne sais pas si c'était la bonne façon.

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

me donne l'erreur

ERREUR: la fonction de décryptage (bytea, unknown, unknown) n'existe pas
LIGNE 1: CHOISIR décrypter (pw, 'clé', 'aes') DE tabelle OERE ID = 7; ^
CONSEIL: Aucune fonction ne correspond au nom et aux types d'arguments donnés. Vous devrez peut-être ajouter des transtypages de types explicites.

Est-ce que cela signifie vraiment que encrypt () est une fonction existante, mais pas decrypt ()? Sinon, comment pourrais-je récupérer des valeurs cryptées aes?

15
32bitfloat

\df *crypt Dans psql révèle les types d'argument des fonctions pgcrypto encrypt et decrypt ( comme le font les documents 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
 ...

les fonctions encrypt et decrypt s'attendent donc à ce que la clé soit bytea. Selon le message d'erreur, "vous devrez peut-être ajouter des transtypages de type explicites".

Cependant, cela fonctionne bien ici sur Pg 9.1, donc je pense qu'il y a plus que ce que vous avez montré. Peut-être avez-vous une autre fonction également nommée encrypt avec trois arguments?

Voici comment cela fonctionne sur une Pg 9.1 propre:

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! Risque d'exposition clé, extrême prudence administrative requise!

BTW, réfléchissez bien si PgCrypto est vraiment le bon choix. Les clés de vos requêtes peuvent être révélées dans pg_stat_activity Et les journaux système via log_statement Ou via des instructions de chiffrement qui échouent avec une erreur. OMI, il est souvent préférable de faire de la cryptographie dans l'application .

Soyez témoin de cette session, avec client_min_messages Activé afin que vous puissiez voir ce qui apparaîtrait dans les journaux:

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)

Oups, clé éventuellement exposée dans les journaux si log_min_messages Est suffisamment bas. Il est maintenant sur le stockage du serveur, avec les données chiffrées. Échouer. Même problème sans log_statement Si une erreur se produit pour provoquer la consignation de l'instruction, ou éventuellement si auto_explain Est activé.

Une exposition via pg_stat_activity Est également possible. Ouvrez deux sessions et:

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

Oups! Voilà encore la clé. Il peut être reproduit sans le LOCK TABLE Par un attaquant non privilégié, il est juste plus difficile de bien le chronométrer. L'attaque via pg_stat_activity Peut être évitée en révoquant l'accès à pg_stat_activity De public, mais cela montre simplement qu'il n'est peut-être pas préférable d'envoyer votre clé à la base de données sauf si vous savez que votre application est la seule chose à y accéder. Même alors, je n'aime pas.

S'il s'agit de mots de passe, devez-vous les stocker?

De plus, si vous stockez des mots de passe, ne les cryptez pas dans les deux sens; si possible sel les mots de passe, puis hachez-les et stockez le résultat . Vous n'avez généralement pas besoin de pouvoir récupérer le texte en clair du mot de passe, confirmez seulement que le hachage stocké correspond au mot de passe que l'utilisateur vous envoie pour vous connecter lorsqu'il est haché avec le même sel.

Si c'est auth, laissez quelqu'un d'autre le faire pour vous

Encore mieux, ne stockez pas du tout le mot de passe, authentifiez-vous contre LDAP, SASL, Active Directory, un fournisseur OAuth ou OpenID, ou tout autre système externe qui est déjà conçu et fonctionne.

Ressources

et beaucoup plus.

16
Craig Ringer