\df *crypt
no psql revela os tipos de argumento do pgcrypto encrypt
e decrypt
funções (assim como os documentos do 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
...
então as funções encrypt
e decrypt
esperam que a chave seja bytea
. Conforme a mensagem de erro, "pode ser necessário adicionar conversões explícitas de tipo".
No entanto, ele funciona bem aqui na página 9.1, então eu suspeito que haja mais do que você mostrou. Talvez você tenha outra função também nomeada encrypt
com três argumentos?
Veja como funciona em uma página 9.1 limpa:
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! Risco de exposição chave, extrema cautela administrativa necessária!
Por favor, pense cuidadosamente se PgCrypto é realmente a escolha certa. As chaves em suas consultas podem ser reveladas pg_stat_activity
e o sistema registra via log_statement
ou através de instruções de criptografia que falham com um erro. Na IMO, geralmente é melhor fazer criptografia no aplicativo .
Testemunhe esta sessão, com client_min_messages
enabled, para que você possa ver o que apareceria nos logs:
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)
Opa, chave possivelmente exposta nos logs se log_min_messages
estiver baixa o suficiente. Agora ele está no armazenamento do servidor, junto com os dados criptografados. Falhou. O mesmo problema, sem a log_statement
ocorrência de um erro para fazer com que a instrução seja registrada, ou possivelmente se auto_explain
estiver ativada.
A exposição via pg_stat_activity
também é possível. Abra duas sessões e:
- 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();
Ops! Lá vai a chave novamente. Ele pode ser reproduzido sem a LOCK TABLE
ajuda de um invasor sem privilégios, é mais difícil cronometrar o tempo certo. O ataque via pg_stat_activity
pode ser evitado revogando o acesso a pg_stat_activity
partir de public
, mas mostra que talvez não seja melhor enviar sua chave ao banco de dados, a menos que você saiba que seu aplicativo é a única coisa que o acessa. Mesmo assim, eu não gosto.
Se são senhas, você deve armazená-las?
Além disso, se você estiver armazenando senhas, não as criptografe nos dois sentidos; se possível, senhas de salt possíveis , faça hash e armazene o resultado . Geralmente, você não precisa recuperar o texto não criptografado da senha, apenas confirme se o hash armazenado corresponde à senha que o usuário envia para efetuar login quando é hash com o mesmo sal.
Se for autenticação, deixe que outra pessoa faça isso por você
Melhor ainda, não armazene a senha, autentique-se no LDAP, SASL, Active Directory, em um provedor OAuth ou OpenID ou em algum outro sistema externo que já esteja projetado e funcionando.
Recursos
e muito mais.