Como usar criptografia Aes no PostgreSQL?

15

Eu tentei a criptografia de aes usando a seguinte instrução:

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

que funcionou, mas não consigo descriptografar o valor. Eu o inseri em um campo do tipo de dados bytea, mas não tenho certeza se esse foi o caminho certo.

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

me dá o erro

ERRO: descriptografia de função (bytea, desconhecido, desconhecido) não existe
LINHA 1: descriptografar SELECT (pw, 'chave', 'aes') FROM tabelle WHERE ID = 7; ^
DICA: Nenhuma função corresponde ao nome e aos tipos de argumento. Pode ser necessário adicionar conversões de tipo explícitas.

Isso realmente significa que encrypt () é uma função existente, mas não descriptografada ()? De que outra forma eu poderia recuperar valores criptografados por aes?

32bitfloat
fonte

Respostas:

16

\df *cryptno psql revela os tipos de argumento do pgcrypto encrypte decryptfunçõ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 encrypte decryptesperam 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 encryptcom 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_activitye o sistema registra via log_statementou 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_messagesenabled, 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_messagesestiver baixa o suficiente. Agora ele está no armazenamento do servidor, junto com os dados criptografados. Falhou. O mesmo problema, sem a log_statementocorrência de um erro para fazer com que a instrução seja registrada, ou possivelmente se auto_explainestiver ativada.

A exposição via pg_stat_activitytambé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 TABLEajuda de um invasor sem privilégios, é mais difícil cronometrar o tempo certo. O ataque via pg_stat_activitypode ser evitado revogando o acesso a pg_stat_activitypartir 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.

Craig Ringer
fonte
Não é mais do que mostrei e não defini novas funções, é um novo postgresql instalado. É bastante irritante que sua amostra e a primeira instrução de seleção que eu publiquei também não funcionem enquanto isso, retornando o mesmo erro que foi postado acima. Em algum lugar, algo deu errado ... de qualquer forma, obrigado pela sua resposta.
32bitfloat
Experimente um CREATEbanco de dados recém- d de template0; por exemplo, CREATE DATABASE testdb TEMPLATE template0então CREATE EXTENSION pgcrypto;e teste. Veja se há algo desonesto no template1.
Craig Ringer
Apenas uma nota sobre descriptografia bidirecional no banco de dados. Eu não acho que seja sempre a direção errada, mas acrescenta complexidade e, em qualquer lugar que você lida com isso, você realmente precisa lidar com o gerenciamento de chaves que pode ser mais complicado no banco de dados.
Chris Travers
Além disso, 100% do segundo da noção de que você NUNCA deve descriptografar senhas e que conectar-se a um sistema mantido por mais pessoas geralmente é uma vitória significativa em termos de segurança.
Chris Travers
3
lol, +1 para "Awooga! Awooga!"
Jeromy French 24/03