pgcrypto — криптографические функции
Эта страница переведена при помощи нейросети GigaChat.
Модуль pgcrypto
предоставляет криптографические функции для PostgreSQL.
Этот модуль считается «надежным», то есть его могут устанавливать обычные пользователи, которые имеют привилегию CREATE
на текущей базе данных.
pgcrypto
требует OpenSSL и не будет установлен, если поддержка OpenSSL не была выбрана при сборке PostgreSQL.
Стандартные функции хеширования
digest()
digest(data text, type text) returns bytea
digest(data bytea, type text) returns bytea
Вычисляет двоичный хеш данных data
. type
– это алгоритм, который следует использовать. Стандартные алгоритмы включают md5
, sha1
, sha224
, sha256
, sha384
и sha512
. Кроме того, любой алгоритм дайджеста, поддерживаемый OpenSSL, автоматически подхватывается.
Если нужно получить дайджест в виде шестнадцатеричной строки, используйте encode()
на результате. Например:
CREATE OR REPLACE FUNCTION sha1(bytea) returns text AS $$
SELECT encode(digest($1, 'sha1'), 'hex')
$$ LANGUAGE SQL STRICT IMMUTABLE;
hmac()
hmac(data text, key text, type text) returns bytea
hmac(data bytea, key bytea, type text) returns bytea
Вычисляет хешированный MAC для data
с ключом key
. type
такой же, как и в digest()
.
Функция похожа на digest()
, но хеш может быть пересчитан только при знании ключа. Это предотвращает сценарий, когда кто-то изменяет данные и также меняет хеш, чтобы он соответствовал.
Если размер ключа больше размера блока хеша, то сначала будет произведен его хеш и результат будет использован в качестве ключа.
Функции хеширования паролей
Функции crypt()
и gen_salt()
специально предназначены для хеширования паролей. crypt()
выполняет хеширование, а gen_salt()
подготавливает параметры алгоритма для него.
Алгоритмы в crypt()
отличаются от обычных алгоритмов хеширования MD5 или SHA1 следующим образом:
- Они медленные. Поскольку объем данных настолько мал, это единственный способ усложнить подбор паролей методом перебора.
- Они используют случайное значение, называемое солью, чтобы пользователи с одинаковым паролем имели разные зашифрованные пароли. Это также дополнительная защита от обращения алгоритма.
- Они включают тип алгоритма в результат, поэтому пароли, хешированные разными алгоритмами, могут сосуществовать.
- Некоторые из них адаптивны – это означает, что когда компьютеры становятся быстрее, можно настроить алгоритм так, чтобы он работал медленнее, не вызывая несовместимости с существующими паролями.
Таблица ниже содержит список алгоритмов, поддерживаемых функцией crypt()
.
Поддерживаемые алгоритмы для crypt():
Алгоритм | Максимальная длина пароля | Адаптивный? | Бит соли | Длина вывода | Описание |
---|---|---|---|---|---|
bf | 72 | да | 128 | 60 | На основе Blowfish, вариант 2a |
md5 | неограниченный | нет | 48 | 34 | Шифрование на основе MD5 |
xdes | 8 | да | 24 | 20 | Расширенный DES |
des | 8 | нет | 12 | 13 | Оригинальная функция crypt в UNIX |
crypt()
crypt(password text, salt text) returns text
Вычисляет хеш в стиле crypt(3) для password
. При сохранении нового пароля нужно использовать gen_salt()
для генерации нового значения salt
. Чтобы проверить пароль, передайте сохраненное значение хеша как salt
и проверьте, совпадает ли результат с сохраненным значением.
Пример установки нового пароля:
UPDATE ... SET pswhash = crypt('new password', gen_salt('md5'));
Пример аутентификации:
SELECT (pswhash = crypt('entered password', pswhash)) AS pswmatch FROM ... ;
Запрос возвращает true
, если введенный пароль правильный.
gen_salt()
gen_salt(type text [, iter_count integer ]) returns text
Генерирует новую случайную строку соли для использования в crypt()
. Строка соли также сообщает crypt()
, какой алгоритм использовать.
Параметр type
задает алгоритм хеширования. Допустимые типы: des
, xdes
, md5
и bf
.
Параметр iter_count
позволяет пользователю указать количество итераций для алгоритмов, которые это поддерживают. Чем выше счетчик, тем больше времени требуется для хеширования пароля и, следовательно, тем больше времени потребуется для его взлома. Хотя при слишком высоком значении время расчета хеша может составлять несколько лет --- что несколько непрактично. Если параметр iter_count
опущен, используется значение по умолчанию. Допустимые значения для iter_count
зависят от алгоритма и показаны в таблице Таблица «Количество итераций для crypt()
».
Таблица «Количество итераций для crypt()
»
Алгоритм | По умолчанию | Мин | Максимум |
---|---|---|---|
xdes | 725 | 1 | 16777215 |
bf | 6 | 4 | 31 |
Для xdes
существует дополнительное ограничение, согласно которому количество итераций должно быть нечетным числом.
Чтобы выбрать подходящую итерационную скорость, следует учитывать, что оригинальная криптография DES была разработана для обеспечения скорости в 4 хеша в секунду на оборудовании того времени. Скорость ниже 4 хешей в секунду, вероятно, снизит удобство использования. Быстрее чем 100 хешей в секунду, вероятно, слишком быстро.
В таблице «Скорость работы алгоритмов хеширования» представлен обзор относительной медлительности различных алгоритмов хеширования. В таблице показано, сколько времени потребовалось бы, чтобы попробовать все комбинации символов в 8-символьном пароле, предполагая, что пароль содержит только строчные буквы или заглавные и строчные буквы и цифры. В записях crypt-bf
число после косой черты является параметром iter_count
из gen_salt
.
Скорость работы алгоритмов хеширования:
Алгоритм | Хеши/сек | Для [a-z] | Для [A-Za-z0-9] | Продолжительность относительно md5 hash |
---|---|---|---|---|
crypt-bf/8 | 1792 | 4 года | 3927 лет | 100 тыс. |
crypt-bf/7 | 3648 | 2 года | 1929 лет | 50 тыс. |
crypt-bf/6 | 7168 | 1 год | 982 года | 25 тыс. |
crypt-bf/5 | 13504 | 188 дней | 521 год | 12,5 тыс. |
crypt-md5 | 171584 | 15 дней | 41 год | 1к |
crypt-des | 23221568 | 157,5 минут | 108 дней | 7 |
sha1 | 37774272 | 90 минут | 68 дней | 4 |
md5 (хеш) | 150085504 | 22,5 минуты | 17 дней | 1 |
Примечания:
- для расчетов использовался процессор Intel Mobile Core i3;
- номера алгоритмов взяты из вывода John the Ripper v1.6.38
-test
; - числа md5 получены от mdcrack 1.2;
- числа sha1 получены от lcrack-20031130-бета.
crypt-bf
числа получены с использованием простой программы, которая проходит по 1000 паролей из 8 символов. Таким образом можно показать скорость с разным количеством итераций. Для справки:john -test
показывает 13506 циклов/сек дляcrypt-bf/5
(очень небольшая разница в результатах соответствует тому факту, что реализацияcrypt-bf
вpgcrypto
является той же самой, что и в John the Ripper).
Обратите внимание, что «попробовать все комбинации»» не является реалистичным упражнением. Обычно взлом пароля осуществляется с помощью словарей, которые содержат как обычные слова, так и различные их мутации. Так что даже несколько похожий на слово пароль может быть взломан гораздо быстрее, чем предполагают приведенные выше цифры, тогда как шестизначный непохожий на слово пароль может избежать взлома. Или нет.
Функции шифрования PGP
Функции здесь реализуют часть шифрования стандарта OpenPGP (RFC 4880). Поддерживаются как симметричное, так и асимметричное шифрование.
Зашифрованное сообщение PGP состоит из двух частей или пакетов:
- Пакет, содержащий сеансовый ключ – зашифрованный симметричным или открытым ключом.
- Пакет, содержащий данные, зашифрованные с помощью сеансового ключа.
При шифровании с использованием симметричного ключа (т.е. пароля):
- Данный пароль хешируется с использованием алгоритма String2Key (S2K). Это довольно похоже на
crypt()
алгоритмы – преднамеренно медленные и со случайной солью – но они производят полный двоичный ключ. - Если запрошен отдельный сеансовый ключ, будет сгенерирован новый случайный ключ. В противном случае ключ S2K будет использоваться непосредственно в качестве сеансового ключа.
- Если ключ S2K должен быть использован напрямую, то только настройки S2K будут помещены в пакет сеансового ключа. В противном случае сеансовый ключ будет зашифрован с помощью ключа S2K и помещен в пакет сеансового ключа.
При шифровании с открытым ключом:
- Новый случайный сеансовый ключ генерируется.
- Он шифруется с использованием открытого ключа и помещается в пакет сеансового ключа.
В любом случае данные для шифрования обрабатываются следующим образом:
- Необязательная обработка данных: сжатие, преобразование в UTF-8 и/или преобразование окончаний строк.
- Данные предваряются блоком случайных байтов. Это эквивалентно использованию случайного IV.
- Хеш SHA1 из случайного префикса и данных добавляется.
- Все это шифруется с помощью сеансового ключа и помещается в пакет данных.
pgp_sym_encrypt()
pgp_sym_encrypt(data text, psw text [, options text ]) returns bytea
pgp_sym_encrypt_bytea(data bytea, psw text [, options text ]) returns bytea
Шифрует data
с помощью симметричного ключа PGP psw
. Параметр options
может содержать параметры настройки, как описано ниже.
pgp_sym_decrypt()
pgp_sym_decrypt(msg bytea, psw text [, options text ]) returns text
pgp_sym_decrypt_bytea(msg bytea, psw text [, options text ]) returns bytea
Расшифровывает сообщения PGP, зашифрованного симметричным ключом.
Расшифровка bytea
данных с использованием pgp_sym_decrypt
запрещена. Это делается для предотвращения вывода недопустимых символьных данных. Расшифровка изначально текстовых данных с помощью pgp_sym_decrypt_bytea
допустима.
Параметр options
может содержать параметры настройки, как описано ниже.
pgp_pub_encrypt()
pgp_pub_encrypt(data text, key bytea [, options text ]) returns bytea
pgp_pub_encrypt_bytea(data bytea, key bytea [, options text ]) returns bytea
Шифрует data
с помощью открытого ключа PGP key
. Передача этой функции секретного ключа приведет к ошибке.
Параметр options
может содержать параметры настройки, как описано ниже.
pgp_pub_decrypt()
pgp_pub_decrypt(msg bytea, key bytea [, psw text [, options text ]]) returns text
pgp_pub_decrypt_bytea(msg bytea, key bytea [, psw text [, options text ]]) returns bytea
Расшифровывает сообщение, зашифрованное открытым ключом. key
должен быть секретным ключом, соответствующим открытому ключу, который использовался для шифрования. Если секретный ключ защищен паролем, необходимо указать пароль в psw
. Если нет пароля, но требуется указать параметры, то нужно предоставить пустой пароль.
Дешифровка bytea
данных с помощью pgp_pub_decrypt
запрещена. Это делается для предотвращения вывода недопустимых символьных данных. Дешифровка первоначально текстовых данных с использованием pgp_pub_decrypt_bytea
допустима.
Параметр options
может содержать настройки параметров, описанные ниже.
pgp_key_id()
pgp_key_id(bytea) returns text
pgp_key_id
извлекает идентификатор ключа общедоступного или секретного ключа PGP. Или он дает идентификатор ключа, который использовался для шифрования данных, если дано зашифрованное сообщение.
Он может вернуть 2 специальных идентификатора ключей:
-
SYMKEY
– сообщение зашифровано с использованием симметричного ключа. -
ANYKEY
– сообщение зашифровано открытым ключом, но идентификатор ключа был удален. Это означает, что нужно будет попробовать все секретные ключи, чтобы увидеть, какой из них его расшифровывает.pgcrypto
сам по себе не создает такие сообщения.
Обратите внимание, что разные ключи могут иметь один и тот же идентификатор. Это редкость, но нормальное событие. Тогда клиентское приложение должно попытаться расшифровать каждый из них, чтобы увидеть, какой подходит – как обработка ANYKEY
.
armor(), dearmor()
armor(data bytea [ , keys text[], values text[] ]) returns text
dearmor(data text) returns bytea
Эти функции упаковывают/распаковывают двоичные данные в формат PGP ASCII-armor, который представляет собой базовый Base64 с CRC и дополнительным форматированием.
Если массивы keys
и values
указаны, заголовок защиты добавляется к защищенному формату для каждой пары ключ/значение. Оба массива должны быть одномерными, и они должны иметь одинаковую длину. Ключи и значения не могут содержать никаких символов, отличных от ASCII.
pgp_armor_headers
pgp_armor_headers(data text, key out text, value out text) returns setof record
pgp_armor_headers()
извлекает заголовки защиты из data
. Возвращаемое значение представляет собой набор строк с двумя столбцами, ключом и значением. Если ключи или значения содержат какие-либо символы, отличные от ASCII, они рассматриваются как UTF-8.
Параметры для функций PGP
Параметры названы так, чтобы быть похожими на GnuPG. Значение параметра должно быть указано после знака равенства. Разделяйте параметры друг от друга запятыми. Например:
pgp_sym_encrypt(data, psw, 'compress-algo=1, cipher-algo=aes256')
Все параметры, кроме convert-crlf
, применяются только к функциям шифрования. Функции дешифровки получают параметры из данных PGP.
Наиболее интересными параметрами, вероятно, являются compress-algo
и unicode-mode
. Остальные должны иметь разумные значения по умолчанию.
cipher-algo
Какой алгоритм шифрования использовать.
Значения: bf, aes128, aes192, aes256, 3des, cast5.
По умолчанию: aes128.
Применимо к: pgp_sym_encrypt, pgp_pub_encrypt.
compress-algo
Какой алгоритм сжатия использовать. Доступно только в том случае, если PostgreSQL был собран с использованием zlib.
Значения: 0 - без сжатия, 1 - сжатие ZIP, 2 - сжатие ZLIB (= ZIP плюс мета-данные и CRC-блоки).
По умолчанию: 0.
Применимо к: pgp_sym_encrypt, pgp_pub_encrypt.
compress-level
Насколько сильно сжимать. Более высокие уровни сжимают меньше, но работают медленнее. 0 отключает сжатие.
Значения: 0, 1-9.
По умолчанию: 6.
Применимо к: pgp_sym_encrypt, pgp_pub_encrypt.
convert-crlf
Нужно ли преобразовывать \n
в \r\n
при шифровании и \r\n
в \n
при расшифровке. RFC 4880 указывает, что текстовые данные должны храниться с использованием \r\n
перевода строки. Используйте это для получения полностью совместимого с RFC поведения.
Значения: 0, 1.
По умолчанию: 0.
Применяется к: pgp_sym_encrypt, pgp_pub_encrypt, pgp_sym_decrypt, pgp_pub_decrypt.
disable-mdc
Не защищать данные с помощью SHA-1. Единственная веская причина использовать этот параметр – обеспечить совместимость со старыми продуктами PGP, предшествующими добавлению пакетов, защищенных SHA-1, к RFC 4880. Современное программное обеспечение gnupg.org и pgp.com поддерживает его отлично.
Значения: 0, 1.
По умолчанию: 0.
Применимо к: pgp_sym_encrypt, pgp_pub_encrypt.
sess-key
Использовать отдельный сеансовый ключ. Шифрование открытым ключом всегда использует отдельный сеансовый ключ. Эта опция предназначена для симметричного шифрования, которое по умолчанию использует ключ S2K напрямую.
Значения: 0, 1.
По умолчанию: 0.
Применяется к: pgp_sym_encrypt.
s2k-mode
Какой алгоритм S2K использовать.
Значения:
- 0 - Без соли. Опасно!
- 1 - С солью, но с фиксированным количеством итераций.
- 3 - Переменное количество итераций.
По умолчанию: 3.
Применимо к: pgp_sym_encrypt.
s2k-count
Количество итераций алгоритма S2K для использования. Это должно быть значение между 1024 и 65011712 включительно.
По умолчанию: случайное значение между 65536 и 253952.
Применимо к: pgp_sym_encrypt, только с s2k-mode=3.
s2k-digest-algo
Какой алгоритм дайджеста использовать в расчете S2K.
Значения: md5, sha1.
По умолчанию: sha1.
Применимо к: pgp_sym_encrypt.
s2k-cipher-algo
Какой шифр использовать для шифрования отдельного сеансового ключа.
Значения: bf, aes, aes128, aes192, aes256.
По умолчанию: use cipher-algo.
Применяется к: pgp_sym_encrypt.
unicode-mode
Следует ли преобразовывать текстовые данные из внутренней кодировки базы данных в UTF-8 и обратно. Если база данных уже является UTF-8, никакого преобразования не будет выполнено, но сообщение будет помечено как UTF-8. Без этого параметра это не будет сделано.
Значения: 0, 1.
По умолчанию: 0.
Применимо к: pgp_sym_encrypt, pgp_pub_encrypt.
Генерация ключей PGP с помощью GnuPG
Для генерации нового ключа:
gpg --gen-key
Предпочтительный тип ключа - «DSA и Elgamal».
Для шифрования RSA нужно создать либо ключ подписи только для DSA, либо ключ подписи только для RSA в качестве основного, а затем добавить подключ RSA-шифрования с помощью gpg --edit-key
.
Чтобы перечислить ключи:
gpg --list-secret-keys
Чтобы экспортировать открытый ключ в формате ASCII-armor:
gpg -a --export KEYID > public.key
Чтобы экспортировать секретный ключ в формате ASCII-armor:
gpg -a --export-secret-keys KEYID > secret.key
Нужно использовать dearmor()
для этих ключей перед тем, как передать их функциям PGP. Или если можно работать с двоичными данными, то можете удалите -a
из команды.
Для получения дополнительной информации см. man gpg
, Справочник по конфиденциальности GNU и другую документацию на https://www.gnupg.org/.
Ограничения кода PGP
- Отсутствие поддержки подписи. Это также означает, что не проверяется, принадлежит ли подстановочный ключ шифрования главному ключу.
- Нет поддержки ключа шифрования в качестве главного ключа. Поскольку такая практика обычно не приветствуется, это не должно быть проблемой.
- Нет поддержки для нескольких подключей. Это может показаться проблемой, поскольку это обычная практика. С другой стороны, нельзя использовать свои обычные ключи GPG/PGP с
pgcrypto
, но создавать новые, так как сценарий использования довольно отличается.
Функции шифрования без обработки
Эти функции просто запускают шифр над данными, у них нет никаких продвинутых функций шифрования PGP. Поэтому у них есть несколько серьезных проблем:
- Они используют пользовательский ключ непосредственно в качестве ключа шифра.
- Они не предоставляют никакой проверки целостности для определения того, было ли изменено зашифрованное данные.
- Они ожидают, что пользователи будут управлять всеми параметрами шифрования самостоятельно, даже вектором инициализации.
- Они не обрабатывают текст.
Таким образом, с введением шифрования PGP использование необработанных функций шифрования не рекомендуется.
encrypt(data bytea, key bytea, type text) returns bytea
decrypt(data bytea, key bytea, type text) returns bytea
encrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
decrypt_iv(data bytea, key bytea, iv bytea, type text) returns bytea
Эти функции зашифровывают/расшифровывают данные с использованием метода шифрования, указанного в type
. Синтаксис строки type
следующий:
algorithm [ - mode ] [ /pad: padding ]
где algorithm
является одним из:
bf
– Blowfish;aes
– AES (Rijndael-128, -192 или -256).
и mode
является одним из:
cbc
– следующий блок зависит от предыдущего (по умолчанию);ecb
– каждый блок шифруется отдельно (только для тестирования).
и padding
является одним из:
pkcs
– данные могут быть любой длины (по умолчанию);none
– данные должны быть кратны размеру блока шифра.
Итак, например, эти вызовы эквивалентны:
encrypt(data, 'fooz', 'bf')
encrypt(data, 'fooz', 'bf-cbc/pad:pkcs')
В encrypt_iv
и decrypt_iv
, параметр iv
является начальным значением для режима CBC, он игнорируется для ECB. Он обрезается или дополняется нулями, если его размер не совпадает точно с размером блока. В функциях без этого параметра он по умолчанию равен всем нулям.
Функции случайных данных
gen_random_bytes(count integer) returns bytea
Возвращает count
криптографически сильные случайные байты. За один раз можно извлечь не более 1024 байт. Это делается для того, чтобы избежать истощения пула генератора случайных чисел.
gen_random_uuid() returns uuid
Возвращает версию 4 (случайный) UUID. (Устарело, эта функция внутренне вызывает основную функцию с тем же именем.)
Примечания
Конфигурирование
pgcrypto
настраивается в соответствии с результатами основного сценария PostgreSQL configure
. Параметры, которые влияют на это, – это --with-zlib
и --with-ssl=openssl
.
Когда компилируется с zlib, функции шифрования PGP могут сжимать данные перед их шифрованием.
pgcrypto
требует OpenSSL. В противном случае он не будет собран или установлен.
При компиляции с использованием OpenSSL версии 3.0.0 и более поздних версий необходимо активировать поставщик устаревших функций в файле конфигурации openssl.cnf
, чтобы использовать старые шифры, такие как DES или Blowfish.
Обработка NULL
Как это стандартно для SQL, все функции возвращают значение NULL, если любой из аргументов равен NULL. Это может создать риски безопасности при небрежном использовании.
Ограничения безопасности
Все функции выполняются внутри сервера базы данных. Это означает, что все данные и пароли передаются между сервером и клиентскими приложениями в виде открытого текста. Таким образом необходимо:
- Подключаться локально или использовать SSL-соединения.
- Доверять как системному администратору, так и администратору базы данных.
Если это невозможно, то лучше выполняйте криптографические операции внутри клиентского приложения.
Реализация не устойчива к атакам с использованием побочных каналов. Например, время, необходимое для завершения функции расшифровки pgcrypto
, варьируется среди шифртекстов заданного размера.