\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_activity
from public
, но это просто показывает, что не стоит отправлять ваш ключ в БД, если вы не знаете, что ваше приложение - единственная вещь, которая когда-либо обращалась к нему. Даже тогда я не люблю.
Если это пароли, стоит ли вообще их хранить?
Кроме того, если вы храните пароли, не шифруйте их двусторонним способом; если это вообще возможно, то хешируйте их и сохраняйте результат . Обычно вам не нужно восстанавливать открытый текст пароля, только убедитесь, что сохраненный хеш совпадает с паролем, который пользователь посылает вам для входа в систему, когда он хешируется с той же солью.
Если это авторизация, пусть кто-нибудь другой сделает это за вас
Более того, не храните пароль вообще, проходите проверку подлинности с использованием LDAP, SASL, Active Directory, поставщика OAuth или OpenID или какой-либо другой внешней системы, которая уже разработана и работает.
Ресурсы
и многое другое.