\df *crypt
dans psql révèle les types d'arguments de pgcrypto encrypt
et des decrypt
fonctions ( 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 soupçonne 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 le système se connecte 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, la clé peut être exposée dans les journaux si elle log_min_messages
est suffisamment basse. 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 elle auto_explain
est activée.
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à à nouveau la clé. Il peut être reproduit sans 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
from 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.