Выбор метода аутентификации для финансового приложения на PostgreSQL

15

Сначала немного предыстории.

Проект LedgerSMB - это проект программного обеспечения для финансового учета с открытым исходным кодом, работающий на PostgreSQL. Мы реализуем очень большой объем бизнес-логики в пользовательских функциях, которые действуют как основной инструмент отображения между методами объекта программы и поведением базы данных. В настоящее время мы используем пользователей базы данных в качестве пользователей для аутентификации, частично по выбору (это позволяет централизованную логику безопасности, так что другие инструменты могут быть написаны и повторно использовать разрешения, предоставленные пользователям), и частично по необходимости (после того, как мы разветвились из SQL-Ledger, там было не так много вариантов переоборудования безопасности на эту кодовую базу).

Это дает нам доступ к разумному количеству опций единой регистрации, к которым имеет доступ PostgreSQL, от LDAP до Kerberos 5. Мы можем даже использовать PAM, когда речь идет о паролях. Это также позволяет нам повторно использовать разрешения при интеграции с другими приложениями или разрешении других клиентских интерфейсов. Для приложения финансового учета это выглядит как чистый выигрыш.

Есть очевидные затраты. Для веб-приложения мы очень ограничены типами http-аутентификации, которые могут поддерживаться. Дайджест, например, полностью отсутствует. BASIC работает, и мы могли бы достаточно легко внедрить KRB5 (я планирую поддерживать это и работать из коробки для 1.4). Очень строгие меры аутентификации не могут быть должным образом обработаны напрямую, хотя мы, возможно, могли бы включить их в случае необходимости (например, BASIC + SSL-сертификат на стороне клиента с cn, совпадающим с именем пользователя и определенным корневым ca).

В то же время мы столкнулись с большим количеством критики, в основном со стороны разработчиков, а иногда со стороны dba, которые говорят мне, что приложение должно быть барьером безопасности, а не базой данных. Я по-прежнему считаю, что меньший периметр безопасности, как правило, лучше, что повторное использование бизнес-логики и логики безопасности сочетаются друг с другом, и мне кажется опасным повторное использование бизнес-логики без повторного использования логики безопасности на том же уровне. программы.

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

Крис Траверс
источник
1
Перекрестная публикация в списке рассылки pgsql-general. Смотрите нить, начинающуюся здесь .
Крейг Рингер

Ответы:

17

Я думаю, что вы объединяете аутентификацию и авторизацию .

Я полностью согласен с тем, что сохранение модели безопасности в БД целесообразно, особенно с учетом того, что LedgerSMB разработан с учетом доступа нескольких клиентов. Если вы не планируете на 3-й уровня с промежуточным слоем имеет идеальный смысл иметь пользователь в качестве ролей базы данных, особенно для что - то вроде учета приложения.

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

Вот как это работает для веб-интерфейса, например:

  • janeподключается к серверу веб-интерфейса и проходит проверку подлинности с использованием любого желаемого метода, например, рукопожатия сертификата клиента HTTPS X.509 и аутентификации DIGEST. Сервер теперь имеет соединение от пользователя, которого он принимает на самом деле jane.

  • Сервер подключается к PostgreSQL, используя фиксированное имя пользователя / пароль (или Kerberos, или что угодно), аутентифицируя себя на сервере db в качестве пользователя webui. Сервер db доверяет webuiаутентифицировать своих пользователей, поэтому webuiему были присвоены соответствующие значения GRANT(см. Ниже).

  • В этом соединении сервер использует SET ROLE jane;уровень авторизации пользователя jane. До тех пор, пока не будет запущено RESET ROLE;другое SET ROLE, соединение работает с теми же правами доступа, что janeи SELECT current_user()отчет, и т jane. Д.

  • Сервер поддерживает связь между подключением к базе данных, к которому он имеет SET ROLEотношение, janeи веб-сеансом для пользователя jane, не позволяя этому соединению PostgreSQL использоваться другими подключениями с другими пользователями без нового SET ROLEпромежуточного соединения.

Теперь вы проходите аутентификацию вне сервера, но сохраняете авторизацию на сервере. Pg должен знать, какие пользователи существуют, но ему не нужны пароли или методы аутентификации для них.

Видеть:

Детали

Сервер webui контролирует выполнение запросов, и он не позволяет janeзапускать сырой SQL (надеюсь!), Поэтому janeне может RESET ROLE; SET ROLE special_admin_user;через веб-интерфейс. Для большей безопасности я бы добавил фильтр операторов на сервер, который отклонил, SET ROLEи RESET ROLEесли соединение не входило или не входило в пул неназначенных соединений.

Вы по-прежнему можете использовать прямую аутентификацию для Pg в других клиентах; Вы можете смешивать и сочетать свободно. Вы просто должны GRANTна webuiпользователя права SET ROLEпользователей , которые могут войти в систему через Интернет , а затем дать этим пользователям любые обычные CONNECTправа, пароли и т.д. , которые вы хотите. Если вы хотите сделать их доступными только через Интернет, REVOKEих CONNECTправа на базу данных (и от public).

Чтобы сделать такое разделение аутентификации / авторизации легким, у меня есть особая роль, assume_any_userкоторую я должен GRANTвыполнять каждый вновь созданный пользователь. Затем я перехожу GRANT assume_any_userк реальному имени пользователя, используемому такими вещами, как доверенный веб-интерфейс, давая им право стать любым пользователем, который им нравится.

Очень важно , чтобы assume_any_userна NOINHERITроль, так что webuiпользователь или любой другой не имеет privilges от своей самости и может действовать только в базе данных , как только это SET ROLEдля реального пользователя. Ни при каких обстоятельствах не должен webuiбыть суперпользователем или владельцем БД .

Если вы используете пул соединений, вы можете использовать SET LOCAL ROLEдля установки роли только внутри транзакции, поэтому вы можете вернуть соединения в пул после COMMITили ROLLBACK. Остерегайтесь, это RESET ROLEвсе еще работает, поэтому все еще не безопасно позволить клиенту запускать любой SQL, который он хочет.

SET SESSION AUTHORIZATIONявляется связанной, но более сильной версией этой команды. Это не требует членства в роли, но это команда только для суперпользователя. Вы не хотите, чтобы ваш веб-интерфейс подключался как суперпользователь. Это может быть отменено с RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULTили SET SESSION AUTHORIZATION theusernameвернуть права суперпользователя , так что это не привилегия , сбрасывая барьер безопасности либо.

Команда, которая работала бы как, SET SESSION AUTHORIZATIONно была необратимой и работала бы, если бы вы были участником роли, но не суперпользователем, было бы здорово. На данный момент их нет, но вы все равно можете разделить аутентификацию и авторизацию, если будете осторожны.

Пример и объяснение

CREATE ROLE dbowner NOLOGIN;
CREATE TABLE test_table(x text);
INSERT INTO test_table(x) VALUES ('bork');
ALTER TABLE test_table OWNER TO dbowner;

CREATE ROLE assume_any_user NOINHERIT NOLOGIN;
CREATE ROLE webui LOGIN PASSWORD 'somepw' IN ROLE assume_any_user;

CREATE ROLE jane LOGIN PASSWORD 'somepw';
GRANT jane TO assume_any_user;
GRANT ALL ON TABLE test_table TO jane;

CREATE ROLE jim LOGIN PASSWORD 'somepw';
GRANT jim TO assume_any_user;

Теперь подключите как webui. Обратите внимание , что вы не можете ничего сделать , test_tableно вы можете SET ROLE с janeи затем вы можете получить доступ к test_table:

$ psql -h 127.0.0.1 -U webui regress
Password for user webui:

regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | webui
(1 row)



regress=> SELECT * FROM test_table;
ERROR:  permission denied for relation test_table

regress=> SET ROLE jane;
SET

regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | jane
(1 row)

regress=> SELECT * FROM test_table;
  x   
------
 bork
(1 row)

Обратите внимание , что webui может SET ROLE , чтобы jim, даже когда уже SET ROLEцифро janeи даже если janeне GRANTэд право принимать на себя роль jim. SET ROLEустанавливает ваш эффективный идентификатор пользователя, но он не удаляет вашу способность к SET ROLEдругим ролям, это свойство роли, к которой вы подключены, а не текущей действующей роли. Следовательно , вы должны тщательно контролировать доступ к SET ROLEи RESET ROLEкомандам. У AFAIK нет никакого способа навсегда SET ROLEустановить соединение, действительно стать целевым пользователем, хотя было бы неплохо иметь его.

Для сравнения:

$ psql -h 127.0.0.1 -U webui regress
Password for user webui:

regress=> SET ROLE jane;
SET

regress=> SET ROLE jim;
SET
regress=> SELECT session_user, current_user;
 session_user | current_user 
--------------+--------------
 webui        | jim
(1 row)

чтобы:

$ psql -h 127.0.0.1 -U jane regress
Password for user jane:

regress=> SET ROLE webui;
ERROR:  permission denied to set role "webui"
regress=> SET ROLE jim;
ERROR:  permission denied to set role "jim"

Это означает, что SET ROLEэто не то же самое, что вход в систему с заданной ролью, о чем вы должны помнить.

webuiможет не SET ROLEдо , dbownerтак как он не был GRANTэд это право:

regress=> SET ROLE dbowner;
ERROR:  permission denied to set role "dbowner"

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

Крейг Рингер
источник
1
Кстати, вы можете посмотреть, как pgbouncerработает для некоторых деталей.
Крейг Рингер
2
О, DISCARD ALLэто еще один способ вернуть права на дефолт. Я действительно хотел бы, чтобы у Пг была SET ROLE NORESETили что-то подобное ...
Крейг Рингер