F.13. credcheck#

F.13. credcheck

F.13. credcheck

F.13.1. О расширении credcheck

Проверка имени пользователя/пароля Tantor SE

Версия: 2.6.0

GitHub

Авторское право (c) 2021-2023 MigOps Inc.

Авторское право (c) 2023 Gilles Darold

F.13.2. Описание

Расширение credcheck Tantor SE позволяет проводить общие проверки учетных данных, которые будут применяться при создании пользователя, при изменении пароля и переименовании пользователя. Используя это расширение, мы можем определить набор правил:

  • разрешить определенный набор учетных данных

  • отклонить определенный тип учетных данных

  • отклонять пароль, который можно легко взломать

  • обеспечивает использование срока действия пароля с минимальным сроком в дни

  • определите политику повторного использования паролей

  • определять количество неудачных попыток аутентификации, допустимых до блокировки пользователя

Это расширение разработано на основе хука check_password_hook из Tantor SE.

Это расширение предоставляет все проверки в виде настраиваемых параметров. Настройки по умолчанию не применяют сложные проверки и пытаются разрешить большинство учетных данных. Используя команду SET credcheck.<check-name> TO <some value>; можно применить новые настройки для проверки учетных данных. Настройки могут быть изменены только суперпользователем.

F.13.3. Установка

Добавьте credcheck в параметр конфигурации shared_preload_libraries в вашем файле postgresql.conf, затем перезапустите базу данных Tantor SE для применения изменений.

Проверьте это расширение в одной сессии, используя команду LOAD 'credcheck'; Tantor SE.

Чтобы включить это расширение для всех сессий, выполните команду CREATE EXTENSION credcheck;.

F.13.4. Проверки

Пожалуйста, найдите ниже список общих проверок, которые мы можем применить к учетным данным.

Проверка Тип Описание Значение настройки Принято Не принято
username_min_length username минимальная длина имени пользователя 4 ✓ abcd ✘ abc
username_min_special имя_пользователя минимальное количество специальных символов 1 ✓ a@bc ✘ abcd
username_min_digit username минимальное количество цифр 1 ✓ a1bc ✘ abcd
username_min_upper username минимальное количество прописных букв 2 ✓ aBC ✘ aBc
username_min_lower username минимальное количество строчных букв 1 ✓ aBC ✘ ABC
username_min_repeat username максимальное количество повторений одного и того же символа 2 ✓ aaBCa ✘ aaaBCa
username_contain_password username имя пользователя не должно содержать пароль вкл ✓ имя пользователя - пароль ✘ имя пользователя + пароль
username_contain username имя пользователя должно содержать один из этих символов а,б,в ✓ ade ✘ efg
username_not_contain username имя пользователя не должно содержать одного из этих символов x,y,z ✓ ade ✘ axf
username_ignore_case username игнорировать регистр при выполнении вышеуказанных проверок включено ✓ Ade ✘ aXf
password_min_length password минимальная длина пароля 4 ✓ abcd ✘ abc
password_min_special password минимальное количество специальных символов 1 ✓ a@bc ✘ abc
password_min_digit password минимальное количество цифр в пароле 1 ✓ a1bc ✘ abc
password_min_upper password минимальное количество символов в верхнем регистре 1 ✓ Abc ✘ abc
password_min_lower password минимальное количество строчных символов 1 ✓ aBC ✘ ABC
password_min_repeat password максимальное количество повторений одного и того же символа 2 ✓ aab ✘ aaab
password_contain_username password пароль не должен содержать пароль включено ✓ пароль - имя пользователя ✘ пароль + имя пользователя
password_contain password пароль должен содержать эти символы а,б,в ✓ ade ✘ xfg
password_not_contain password пароль не должен содержать эти символы x,y,z ✓ abc ✘ axf
password_ignore_case password игнорировать регистр при выполнении вышеуказанных проверок включено ✓ Abc ✘ aXf
password_valid_until password принудительное использование предложения VALID UNTIL в операторе CREATE ROLE с минимальным количеством дней 60 ✓ CREATE ROLE abcd VALID UNTIL (now()+3 months::interval)::date ✘ CREATE ROLE abcd LOGIN;
password_valid_max password принудительное использование предложения VALID UNTIL в операторе CREATE ROLE с максимальным числом дней 365 ✓ CREATE ROLE abcd VALID UNTIL (now()+6 months::interval)::date ✘ CREATE ROLE abcd VALID UNTIL (now()+2 years::interval)::date;

Существует также GUC credcheck.whitelist, который можно использовать для установки списка имен пользователей, разделенных запятыми, чтобы исключить их из проверки парольной политики. Например:

credcheck.whitelist = 'admin,supuser'

отключит любую политику проверки учетных данных для пользователя с именем admin и supuser.

F.13.5. Примеры

Давайте начнем с простой проверки, так как каждое имя пользователя должно быть длиной не менее 4 символов.

postgres=# SHOW credcheck.username_min_length;
 credcheck.username_min_length 
-------------------------------
 4
(1 row)

postgres=# CREATE USER abc WITH PASSWORD 'pass';
ERROR:  username length should match the configured credcheck.username_min_length

postgres=# CREATE USER abcd WITH PASSWORD 'pass';
CREATE ROLE

Давайте добавим еще одну проверку, так как каждое имя пользователя должно содержать специальный символ в нем.

postgres=# SHOW credcheck.username_min_special;
 credcheck.username_min_special 
--------------------------------
 1
(1 row)

postgres=# CREATE USER abcd WITH PASSWORD 'pass';
ERROR:  username does not contain the configured credcheck.username_min_special characters

postgres=# CREATE USER abcd$ WITH PASSWORD 'pass';
CREATE ROLE

Давайте добавим еще одну проверку для имени пользователя, где имя пользователя не должно содержать более 1 соседнего повторяющегося символа.

postgres=# show credcheck.username_min_repeat ;
 credcheck.username_min_repeat 
-------------------------------
 1
(1 row)

postgres=# CREATE USER week$ WITH PASSWORD 'pass';
ERROR:  username characters are repeated more than the configured credcheck.username_min_repeat times

postgres=# CREATE USER weak$ WITH PASSWORD 'pass';
CREATE ROLE

postgres=# SHOW credcheck.username_min_repeat ;
 credcheck.username_min_repeat 
-------------------------------
 2
(1 row)

postgres=# CREATE USER week$ WITH PASSWORD 'pass';
CREATE ROLE

Теперь давайте добавим некоторые проверки для пароля. Давайте начнем с проверки, что пароль не должен содержать следующие символы (!@=$#).

postgres=# SHOW credcheck.password_not_contain ;
 credcheck.password_not_contain 
--------------------------------
 !@=$#
(1 row)

postgres=# CREATE USER abcd$ WITH PASSWORD 'p@ss';
ERROR:  password does contain the configured credcheck.password_not_contain characters

postgres=# CREATE USER abcd$ WITH PASSWORD 'pass';
CREATE ROLE

Давайте добавим еще одну проверку для пароля, так как пароль не должен содержать имя пользователя.

postgres=# SHOW credcheck.password_contain_username ;
 credcheck.password_contain_username 
-------------------------------------
 on
(1 row)

postgres=# CREATE USER abcd$ WITH PASSWORD 'abcd$xyz';
ERROR:  password should not contain username

-- OK, ignore case is disabled
postgres=# CREATE USER abcd$ WITH PASSWORD 'ABCD$xyz';
CREATE ROLE

postgres=# CREATE USER abcd$ WITH PASSWORD 'axyz';
CREATE ROLE

Давайте проведем проверки, игнорируя регистр.

postgres=# SHOW credcheck.password_ignore_case;
 credcheck.password_ignore_case 
--------------------------------
 on
(1 row)

postgres=# CREATE USER abcd$ WITH PASSWORD 'ABCD$xyz';
ERROR:  password should not contain username

postgres=# CREATE USER abcd$ WITH PASSWORD 'A$xyz';
CREATE ROLE

Давайте добавим еще одну проверку для пароля, так как пароль не должен содержать повторяющихся символов, расположенных рядом.

postgres=# SHOW credcheck.password_min_repeat ;
 credcheck.password_min_repeat 
-------------------------------
 3
(1 row)

postgres=# CREATE USER abcd$ WITH PASSWORD 'straaaangepaasssword';
ERROR:  password characters are repeated more than the configured credcheck.password_min_repeat times

postgres=# CREATE USER abcd$ WITH PASSWORD 'straaangepaasssword';
CREATE ROLE

credcheck также может обеспечить использование срока действия пароля путем проверки опции VALID UNTIL, используемой в командах CREATE или ALTER ROLE.

postgres=# SET credcheck.password_valid_until = 30;
SET

postgres=# SET credcheck.password_valid_max = 180;
SET

postgres=# CREATE USER abcd$;
ERROR:  require a VALID UNTIL option with a date older than 30 days

postgres=# CREATE USER abcd$ VALID UNTIL '2022-12-21';
ERROR:  require a VALID UNTIL option with a date older than 30 days

postgres=# ALTER USER abcd$ VALID UNTIL '2022-12-21';
ERROR:  require a VALID UNTIL option with a date older than 30 days

postgres=# ALTER USER abcd$ VALID UNTIL '2025-12-21';
ERROR:  require a VALID UNTIL option with a date not beyond 180 days

F.13.6. Политика повторного использования паролей

Tantor SE поддерживает отслеживания срока действия пароля, все другие виды принудительного применения парольной политики предоставляются с помощью расширений. С расширением credcheck, можно принудительно установить длину пароля, задать обязательное определенное количество различных типов символов и сопоставлять его с именем в учетной записи пользователя.

Но одной вещи не хватало, отсутствовала политика запрета повторного использования паролей. Это означало, что когда пользователю требовалось изменить свой пароль, он мог просто повторно использовать свой текущий пароль!

Расширение credcheck добавляет Политику повторного использования паролей в версии 1.0. Чтобы использовать эту функцию, расширение credcheck ДОЛЖНО быть добавлено в параметр конфигурации shared_preload_libraries.

Все пароли пользователей историруются в общей памяти вместе с отметками времени, когда эти пароли были установлены. История паролей сохраняется в файле с именем $PGDATA/global/pg_password_history для загрузки в общую память при запуске. Этот файл должен быть включен в резервные копии, если вы не хотите потерять историю паролей, надеюсь, pg_basebackup позаботится об этом. Пароли хранятся и сравниваются в виде хешей sha256, никогда в открытом виде.

Размер списка использованных паролей по умолчанию установлен на 65535 записей и может быть скорректирован с помощью директивы конфигурации credcheck.history_max_size. Изменение этого GUC требует перезапуска Tantor SE. Одна запись в списке занимает 144 байта, поэтому по умолчанию выделяется около 10 МБ дополнительной общей памяти для списка использованных паролей.

Два параметра позволяют управлять поведением этой функции:

  • credcheck.password_reuse_history: количество различных паролей, установленных до того, как пароль может быть повторно использован.

  • credcheck.password_reuse_interval: количество времени, необходимое, прежде чем пароль можно будет использовать снова.

Значение по умолчанию для этих настроек равно 0, что означает, что все политики повторного использования паролей отключены.

История паролей состоит из паролей, которые пользователь использовал в прошлом. Credcheck может ограничить выбор новых паролей из этой истории:

  • Если учетная запись ограничена на основе количества изменений пароля, новый пароль не может быть выбран из password_reuse_history самых последних паролей. Например, если минимальное количество изменений пароля установлено равным 3, новый пароль не может совпадать с любым из трех самых последних паролей.

  • Если учетная запись ограничена по прошедшему времени, новый пароль не может быть выбран из паролей в истории, которые новее, чем password_reuse_interval дней. Например, если интервал повторного использования пароля установлен на 365, новый пароль не должен быть среди ранее выбранных в течение последнего года.

Чтобы иметь возможность просматривать содержимое истории, в базе данных, в которой вы создали расширение credcheck, предоставляется представление. Представление называется public.pg_password_history. Это представление видно всем.

Суперпользователь также может сбросить содержимое истории паролей, вызвав функцию с именем public.pg_password_history_reset(). Если она вызывается без аргумента, вся история паролей будет очищена. Чтобы удалить только записи, зарегистрированные для одного пользователя, просто передайте его имя в качестве параметра. Эта функция возвращает количество удаленных записей из истории.

Пример:

SET credcheck.password_reuse_history = 2;
CREATE USER credtest WITH PASSWORD 'H8Hdre=S2';
ALTER USER credtest PASSWORD 'J8YuRe=6O';
SELECT rolename, password_hash FROM pg_password_history WHERE rolename = 'credtest' ORDER BY password_date;
 rolename |                          password_hash
----------+------------------------------------------------------------------
 credtest | 7488570b80076cf9da26644d5eeb316c4768ff5bee7bf319344e7bb328032098
 credtest | e61e58c22aa6bf31a92b385932f7d0e4dbaba24fa3fdb2982510d6c72a961335
(2 rows)

-- fail, the credential is still in the history
ALTER USER credtest PASSWORD 'J8YuRe=6O';
ERROR:  Cannot use this credential following the password reuse policy

-- Reset the password history
SELECT pg_password_history_reset();
 pg_password_history_reset
---------------------------
                         2
(1 row)

Пример для интервала повторного использования пароля:

SET credcheck.password_reuse_history = 1;
SET credcheck.password_reuse_interval = 365;
-- Add a new password in the history and set its age to 100 days
ALTER USER credtest PASSWORD 'J8YuRe=6O';
SELECT pg_password_history_timestamp('credtest', now()::timestamp - '100 days'::interval);
 pg_password_history_timestamp
-------------------------------
                             1
(1 row)

SELECT * FROM pg_password_history WHERE rolename = 'credtest';
 rolename |         password_date         |                          password_hash                           
----------+-------------------------------+------------------------------------------------------------------
 credtest | 2022-12-15 13:41:06.736775+03 | c38cf85ca6c3e5ee72c09cf0bfb42fb29b0f0a3e8ba335637941d60f86512508
(1 row)

-- fail, the password is in the history for less than 1 year
ALTER USER credtest PASSWORD 'J8YuRe=6O';
ERROR:  Cannot use this credential following the password reuse policy
-- Change the age of the password to exceed the 1 year interval
SELECT pg_password_history_timestamp('credtest', now()::timestamp - '380 days'::interval);
 pg_password_history_timestamp
-------------------------------
                             2
(1 row)

-- success, the old password present in the history has expired and will be removed
ALTER USER credtest PASSWORD 'J8YuRe=6O';
SELECT rolename, password_hash FROM pg_password_history WHERE rolename = 'credtest';
 rolename |         password_date         |                          password_hash                           
----------+-------------------------------+------------------------------------------------------------------
 credtest | 2023-03-25 13:42:37.387629+03 | c38cf85ca6c3e5ee72c09cf0bfb42fb29b0f0a3e8ba335637941d60f86512508
(1 row)

Функция pg_password_history_timestamp() предназначена только для тестирования и позволяет суперпользователю изменить временную метку всех зарегистрированных паролей в истории.

F.13.7. Блокировка при неудачной аутентификации

Tantor SE не имеет механизма для ограничения количества попыток аутентификации до того, как пользователь будет заблокирован. С расширением credcheck, после определенного количества неудачных попыток аутентификации, заданных директивой конфигурации credcheck.max_auth_failure, пользователь может быть заблокирован и никогда не сможет не подключиться, даже если он предоставит правильный пароль позже.

Расширение credcheck добавляет функцию "Блокировка при неудачной аутентификации" в релизе 2.0. Чтобы использовать эту функцию, расширение credcheck ДОЛЖНО быть добавлено в опцию конфигурации shared_preload_libraries.

Все неудачные попытки аутентификации пользователей регистрируются в общей памяти, включая временные метки о том, когда пользователь был заблокирован. История неудачных попыток аутентификации сохраняется только в памяти, что означает, что история теряется при перезапуске Tantor SE. Возможность восстанавливать кеш при запуске не была признана необходимой.

Размер кеша неудачных попыток аутентификации по умолчанию установлен на 1024 записи и может быть скорректирован с помощью директивы конфигурации credcheck.auth_failure_cache_size. Изменения этого GUC требуют перезапуска Tantor SE.

Два параметра управляют поведением этой функции:

  • credcheck.max_auth_failure: максимальное количество неудачных попыток аутентификации перед блокировкой.

  • credcheck.reset_superuser: не допускает блокировку суперпользователя или восстанавливает заблокированного суперпользователя при установке в true.

Значение по умолчанию для первой настройки - 0, что означает, что функция блокировки при неудачной аутентификации отключена. Значение по умолчанию для второй настройки - false, что означает, что суперпользователь postgres может быть заблокирован.

В случае, если суперпользователь postgres заблокирован и не может войти в систему, и если нет другого аккаунта суперпользователя, который может быть использован для сброса записи заблокированного суперпользователя, установите директиву конфигурации credcheck.reset_superuser в значение true в файле postgresql.conf и отправьте сигнал SIGHUP процессу Tantor SE по pid, чтобы он перечитал конфигурацию. В следующий раз, когда суперпользователь попытается подключиться, его запись в кеше ошибок аутентификации будет удалена.

Пример: kill -1 1234

Суперпользователь также может сбросить содержимое кеша заблокированных пользователей, вызвав функцию public.pg_banned_role_reset(). Если она вызывается без аргумента, весь кеш будет очищен. Чтобы удалить запись, зарегистрированную для одного пользователя, передайте его имя в качестве параметра. Эта функция возвращает количество записей, удаленных из кеша. Перезапуск Tantor SE также очищает кеш.

Пример:

$ psql -h localhost -U toban_user -d gilles
Password for user toban_user: 
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "toban_user"
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "toban_user"

$ psql -h localhost -U toban_user -d gilles
Password for user toban_user: 
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  rejecting connection, user 'toban_user' has been banned
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  rejecting connection, user 'toban_user' has been banned

test=# SELECT * FROM pg_banned_role;
roleid | failure_count |        banned_date         
--------+---------------+----------------------------
250362 |             2 | 2023-06-09 20:33:58.490273
(1 row)

test=# SELECT pg_banned_role_reset();
pg_banned_role_reset 
----------------------
                1
(1 row)

test=# SELECT * FROM pg_banned_role;
roleid | failure_count | banned_date 
--------+---------------+-------------
(0 rows)

После очистки кеша будет разрешена еще одна попытка входа:

$ psql -h localhost -U toban_user -d gilles
Password for user toban_user: 
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "toban_user"
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "toban_user"

F.13.8. Задержка аутентификации

Эта функция позволяет сделать паузу при сбое аутентификации. Установка credcheck.auth_delay_ms вызывает задержку сервера на заданное количество миллисекунд перед сообщением о сбое аутентификации. Это усложняет атаки методом подбора паролей к базе данных.

F.13.9. Ограничения

Это расширение работает только для обычных текстовых паролей.

Пример:

postgres=# CREATE USER user1 PASSWORD 'this is some plain text';
CREATE ROLE

Если какой-либо пользователь попытается создать пользователя с зашифрованным паролем, будет выдана ошибка.

Пример:

postgres=# CREATE USER user1 PASSWORD 'md55e4cc86d2d6a8b73bbefc4d5b91baa45';
ERROR:  password type is not a plain text

Чтобы разрешить использование зашифрованного пароля в CREATE или ALTER ROLE, включите настраиваемую переменную конфигурации credcheck.encrypted_password_allowed.

Проверки имени пользователя не будут применяться при создании пользователя без пароля и при переименовании пользователя, если у пользователя не определен пароль.

Пример (проверки имени пользователя здесь не вызываются):

postgres=# CREATE USER user1;

Пример (проверки имени пользователя здесь не вызываются):

postgres=# ALTER USER user1 RENAME to test_user;

Пример (проверки имени пользователя будут вызываться здесь и в операторе переименования):

postgres=# CREATE USER user1 PASSWORD 'this is some plain text';
CREATE ROLE
postgres=# ALTER USER user1 RENAME to test_user;

F.13.10. Авторы

  • Dinesh Kumar

  • Gilles Darold

Сопровождающий: Жиль Дарольд (Gilles Darold)

F.13.11. Лицензия

Это расширение является свободным программным обеспечением, распространяемым под лицензией PostgreSQL.

Copyright (c) 2021-2023 MigOps Inc.

F.13.12. Заслуги

  • Благодарности автору расширения passwordcheck

  • Благодаря автору расширения password policy

  • Благодарности автору блога Mickael Paquier, доступному по ссылке здесь