Как использовать aes-шифрование в PostgreSQL?

15

Я попытался AES-шифрование с помощью следующего заявления:

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

который работал, но я не могу расшифровать значение. Я вставил его в поле типа данных bytea, но я не уверен, что это правильный путь.

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

дает мне ошибку

ОШИБКА: функция decrypt (bytea, unknown, unknown) не существует.
LINE 1: SELECT decrypt (pw, 'key', 'aes') FROM tabelle WHERE ID = 7; ^
СОВЕТ: ни одна функция не соответствует заданному имени и типу аргумента. Возможно, вам придется добавить явные приведения типов.

Означает ли это, что encrypt () является существующей функцией, но не decrypt ()? Как еще я могу получить зашифрованные значения?

32bitfloat
источник

Ответы:

16

\df *cryptв psql показывает типы аргументов pgcrypto encryptи decryptфункций ( как и документы 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
 ...

так что encryptиdecrypt функции ожидают , что ключ будет bytea. Согласно сообщению об ошибке, «вам может потребоваться добавить явное приведение типов».

Тем не менее, здесь, на Pg 9.1, все работает нормально, поэтому я подозреваю, что в этом есть нечто большее, чем вы показали. Возможно, у вас есть еще одна функция с именемencrypt с тремя аргументами?

Вот как это работает на чистой Pg 9.1:

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! Ключевой риск воздействия, требуется крайняя осторожность администратора!

Кстати, пожалуйста, подумайте, действительно ли PgCrypto является правильным выбором. Ключи в ваших запросах могут быть обнаружены в pg_stat_activityсистеме, и система регистрируется с помощью log_statementили с помощью крипто-операторов, которые завершаются ошибкой. ИМО часто лучше делать крипто в приложении .

Посмотрите этот сеанс с client_min_messagesвключенным, чтобы вы могли видеть, что появилось в журналах:

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)

К сожалению, ключ, возможно, отображается в журналах, если log_min_messagesон достаточно низкий. Теперь он находится в хранилище сервера вместе с зашифрованными данными. Провал. Та же проблема без того, log_statementесли возникает ошибка, заставляющая оператор регистрироваться, или, возможно, если auto_explainон включен.

Экспозиция pg_stat_activityтакже возможна. Откройте две сессии и:

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

Упс! Там снова идет ключ. Он может быть воспроизведен без LOCK TABLEпомощи непривилегированного злоумышленника, просто сложнее определить его время правильно. Атаки с помощью via pg_stat_activityможно избежать, отменив доступ к pg_stat_activityfrom public, но это просто показывает, что не стоит отправлять ваш ключ в БД, если вы не знаете, что ваше приложение - единственная вещь, которая когда-либо обращалась к нему. Даже тогда я не люблю.

Если это пароли, стоит ли вообще их хранить?

Кроме того, если вы храните пароли, не шифруйте их двусторонним способом; если это вообще возможно, то хешируйте их и сохраняйте результат . Обычно вам не нужно восстанавливать открытый текст пароля, только убедитесь, что сохраненный хеш совпадает с паролем, который пользователь посылает вам для входа в систему, когда он хешируется с той же солью.

Если это авторизация, пусть кто-нибудь другой сделает это за вас

Более того, не храните пароль вообще, проходите проверку подлинности с использованием LDAP, SASL, Active Directory, поставщика OAuth или OpenID или какой-либо другой внешней системы, которая уже разработана и работает.

Ресурсы

и многое другое.

Крейг Рингер
источник
Это не больше, чем я показал, и я не определил новые функции, это новый установленный postgresql. Весьма раздражает, что ваш пример и первое опубликованное предложение выбора также не работают, возвращая ту же ошибку, что и выше. Где-то что-то пошло не так ... все равно спасибо за ваш ответ.
32-битное плавание
Попробуйте новую CREATEбазу данных template0; например CREATE DATABASE testdb TEMPLATE template0тогда CREATE EXTENSION pgcrypto;и протестируй. Посмотрите, есть ли что-то хитрое в template1.
Крейг Рингер
Просто примечание о двусторонней расшифровке в БД. Я не думаю, что это всегда неправильное направление, но оно добавляет сложности, и где бы вы ни столкнулись с этим, вам действительно приходится иметь дело с управлением ключами, которое может быть более сложным в БД.
Крис Треверс
Также на 100% второе мнение о том, что вы НИКОГДА не должны расшифровывать пароли и что подключение к системе, поддерживаемой большим количеством людей, обычно является значительным выигрышем с точки зрения безопасности.
Крис Треверс
3
лол, +1 за "Awooga! Awooga!"
Джером Френч