Comment utiliser le cryptage AES dans PostgreSQL?

15

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' inséra dans un champ de type de données bytea mais je ne suis pas sûr si cela é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: SELECT decrypt (pw, 'key', 'aes') FROM tabelle WHERE 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 chiffrées aes?

32bitfloat
la source

Réponses:

16

\df *cryptdans psql révèle les types d'arguments de pgcrypto encryptet des decryptfonctions ( 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 encryptet decrypts'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 soupçonne qu'il y a plus que ce que vous avez montré. Peut-être avez-vous une autre fonction également nommée encryptavec 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_activityet le système se connecte via log_statementou 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_messagesactivé 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, la clé peut être exposée dans les journaux si elle log_min_messagesest suffisamment basse. Il est maintenant sur le stockage du serveur, avec les données chiffrées. Échouer. Même problème sans log_statementsi une erreur se produit pour provoquer la consignation de l'instruction, ou éventuellement si elle auto_explainest activée.

Une exposition via pg_stat_activityest é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à à nouveau la clé. Il peut être reproduit sans LOCK TABLEpar un attaquant non privilégié, il est juste plus difficile de bien le chronométrer. L'attaque via pg_stat_activitypeut être évitée en révoquant l'accès à pg_stat_activityfrom 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 qui y accède. Même alors, je n'aime pas.

S'il s'agit de mots de passe, devriez-vous les stocker du tout?

De plus, si vous stockez des mots de passe, ne les cryptez pas dans les deux sens; si tous les mots de passe possibles sont salés, hachez-les et stockez le résultat . Vous n'avez généralement pas besoin de pouvoir récupérer le mot de passe en clair, 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 déjà conçu et fonctionnel.

Ressources

et beaucoup plus.

Craig Ringer
la source
Ce n'est pas plus que ce que j'ai montré, et je n'ai pas défini de nouvelles fonctions, c'est un nouveau postgresql installé. Il est assez irritant que votre échantillon et la première instruction de sélection que j'ai publiée ne fonctionnent pas entre-temps, renvoyant la même erreur que celle indiquée ci-dessus. Quelque part quelque chose s'est mal passé ... merci quand même pour votre réponse.
32bitfloat
Essayez une nouvelle CREATEbase de données d à partir de template0; par exemple, CREATE DATABASE testdb TEMPLATE template0puis CREATE EXTENSION pgcrypto;et test. Voyez s'il y a quelque chose de douteux dans template1.
Craig Ringer
Juste une note concernant le décryptage bidirectionnel dans la base de données. Je ne pense pas que ce soit toujours la mauvaise direction, mais cela ajoute de la complexité et partout où vous traitez cela, vous devez vraiment gérer la gestion des clés, ce qui peut être plus compliqué dans la base de données.
Chris Travers
De plus, à 100%, l'idée que vous ne devriez JAMAIS avoir à décrypter les mots de passe et que se connecter à un système géré par plus de personnes est généralement un gain de sécurité important.
Chris Travers
3
lol, +1 pour "Awooga! Awooga!"
Jeromy French