Elección del enfoque de autenticación para la aplicación financiera en PostgreSQL

15

Primero algunos antecedentes.

El proyecto LedgerSMB es un proyecto de software de contabilidad financiera de código abierto que se ejecuta en PostgreSQL. Implementamos una gran cantidad de lógica de negocios en funciones definidas por el usuario, que actúan como la herramienta principal de mapeo entre los métodos de objeto de programa y el comportamiento de la base de datos. Actualmente utilizamos usuarios de la base de datos como usuarios de autenticación, en parte por elección (esto permite una lógica de seguridad centralizada, de modo que se pueden escribir otras herramientas y reutilizar los permisos otorgados a los usuarios), y en parte por necesidad (después de bifurcar desde SQL-Ledger, hay no había muchas opciones para actualizar la seguridad en esa base de código).

Esto nos da acceso a un número razonable de opciones de inicio de sesión único a las que PostgreSQL tiene acceso, desde LDAP hasta Kerberos 5. Incluso podemos usar PAM cuando se trata de contraseñas. También nos permite reutilizar los permisos al integrarnos con otras aplicaciones o al permitir otras interfaces de clientes. Para una aplicación de contabilidad financiera, esto parece una ganancia neta.

Hay costos obvios involucrados. Para la aplicación web, estamos muy limitados a los tipos de autenticación http que pueden admitirse. DIGEST, por ejemplo, está completamente fuera. BASIC funciona, y podríamos implementar KRB5 con bastante facilidad (planeo tener esto soportado y trabajar fuera de la caja para 1.4). Las medidas de autenticación muy sólidas no se pueden gestionar correctamente en esto directamente, aunque probablemente podríamos imponerlas si es necesario (por ejemplo, el certificado SSL BASIC + del lado del cliente con un cn que coincida con el nombre de usuario y una raíz raíz específica).

Al mismo tiempo, nos hemos encontrado con una buena cantidad de críticas, principalmente de la multitud de desarrollo y, más ocasionalmente, de los dba que me dicen que la aplicación debería ser la barrera de seguridad, no la base de datos. Mi opinión sigue siendo que un perímetro de seguridad más pequeño es generalmente mejor, que la reutilización de la lógica empresarial y la lógica de seguridad van juntas, y que me parece peligroso reutilizar la lógica empresarial sin reutilizar la lógica de seguridad en el mismo nivel Del programa.

¿Me estoy perdiendo algunas compensaciones importantes aquí? ¿Hay problemas que no estoy considerando?

Chris Travers
fuente
1
Publicación cruzada en la lista de correo pgsql-general. Ver el hilo que comienza aquí .
Craig Ringer

Respuestas:

17

Creo que estás combinando autenticación y autorización .

Estoy completamente de acuerdo en que es aconsejable mantener el modelo de seguridad en la base de datos, especialmente porque LedgerSMB está diseñado teniendo en cuenta el acceso de varios clientes. A menos que planee ir a 3 niveles con una capa de middleware, tiene mucho sentido tener usuarios como roles de base de datos, especialmente para algo como una aplicación de contabilidad.

Esto no significa que deba autenticar usuarios en la base de datos utilizando un método de autenticación compatible con PostgreSQL. Los usuarios, roles y subvenciones de su base de datos pueden usarse para autorización solo si lo desea.

Así es como funciona para una interfaz de usuario web, por ejemplo:

  • janese conecta al servidor web ui y se autentica utilizando el método que desee, por ejemplo, el protocolo de enlace de cliente HTTPS X.509 y la autenticación DIGEST. El servidor ahora tiene una conexión de un usuario que acepta es realmente jane.

  • El servidor se conecta a PostgreSQL usando un nombre de usuario / contraseña fijos (o Kerberos o lo que quiera), autenticándose en el servidor db como el usuario webui. El servidor de db confía webuien autenticar a sus usuarios, por lo que webuise le ha otorgado el correo electrónico apropiado GRANT(ver más abajo).

  • En esa conexión, el servidor utiliza SET ROLE jane;para asumir el nivel de autorización del usuario jane. Hasta que se ejecute RESET ROLE;u otro SET ROLE, la conexión está operando con los mismos derechos de acceso que janey SELECT current_user()etc. informará jane.

  • El servidor mantiene la asociación entre la conexión de base de datos en la que tiene SET ROLEa janey la sesión de la web para el usuario jane, que no permite que la conexión PostgreSQL para ser utilizado por otras conexiones con otros usuarios sin un nuevo SET ROLEintermedio.

Ahora se está autenticando fuera del servidor, pero mantiene la autorización en el servidor. Pg necesita saber qué usuarios existen, pero no necesita contraseñas o métodos de autenticación para ellos.

Ver:

Detalles

El servidor webui controla la ejecución de las consultas, y no permitirá janeejecutar SQL sin procesar (¡espero!), Por lo janeque no puede hacerlo a RESET ROLE; SET ROLE special_admin_user;través de la interfaz de usuario web. Para mayor seguridad, agregaría un filtro de declaración al servidor que rechazó SET ROLEy, a RESET ROLEmenos que la conexión estuviera o ingresara a un grupo de conexiones no asignadas.

Todavía puede usar la autenticación directa a Pg en otros clientes; Puedes mezclar y combinar libremente. Solo tiene que tener los derechos GRANTdel webuiusuario SET ROLEpara los usuarios que pueden iniciar sesión a través de la web y luego otorgar a esos usuarios los CONNECTderechos normales , contraseñas, etc. que desee. Si desea hacerlos solo web, REVOKEsus CONNECTderechos en la base de datos (y desde public).

Para hacer una fracción de esa autenticación / autorización fácil que tengo un papel especial assume_any_userque GRANTtodos los recién creados por el usuario. Luego GRANT assume_any_userutilizo el nombre de usuario real utilizado por cosas como un front-end web confiable, dándoles los derechos para convertirse en el usuario que deseen.

Es importante hacer assume_any_userun NOINHERITrol, para que el webuiusuario o lo que sea no tenga privilegios por sí mismo y solo pueda actuar en la base de datos una vez que sea SET ROLEpara un usuario real. Bajo ninguna circunstancia debe webuiser un superusuario o propietario de DB .

Si está agrupando conexiones, puede usarlo SET LOCAL ROLEpara establecer el rol solo dentro de una transacción, de modo que pueda devolver las conexiones al grupo después COMMITo ROLLBACK. Tenga en cuenta que RESET ROLEtodavía funciona, por lo que todavía no es seguro dejar que el cliente ejecute el SQL que quiera.

SET SESSION AUTHORIZATIONes la versión relacionada pero más fuerte de este comando. No requiere membresía de roles, pero es un comando solo de superusuario. No desea que su interfaz de usuario web se conecte como superusuario. Se puede revertir con RESET SESSION AUTHORIZATION, SET SESSION AUTHORIZATION DEFAULTo SET SESSION AUTHORIZATION theusernamepara recuperar los derechos de superusuario, por lo que tampoco es una barrera de seguridad de privilegio.

Un comando que funcionó de manera similar SET SESSION AUTHORIZATIONpero que era irreversible y que funcionaría si fuera un miembro de rol pero no un superusuario sería genial. En este punto no hay ninguno, pero aún puede separar la autenticación y la autorización bastante bien si tiene cuidado.

Ejemplo y explicacion

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;

Ahora conéctate como webui. Tenga en cuenta que no puede hacer nada, test_tablepero puede SET ROLE hacerlo janey luego puede acceder a 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)

Tenga en cuenta que webui puede SET ROLE que jim, incluso cuando ya SET ROLED para janey aunque janeno se ha GRANTcado el derecho de asumir el papel jim. SET ROLEestablece su identificación de usuario efectiva, pero no elimina su capacidad para SET ROLEotros roles, esa es una propiedad del rol al que se conectó, no su rol efectivo actual. En consecuencia, debe controlar cuidadosamente el acceso a los comandos SET ROLEy RESET ROLE. AFAIK, no hay forma SET ROLEde una conexión permanente , convirtiéndose realmente en el usuario objetivo, aunque ciertamente sería bueno tenerlo.

Comparar:

$ 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)

a:

$ 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"

Esto significa que SET ROLEno es exactamente lo mismo que iniciar sesión como un rol determinado, algo que debe tener en cuenta.

webuipuede no SET ROLEa dbowner, ya que no se ha GRANTed ese derecho:

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

así que por sí mismo es bastante impotente, solo puede asumir los derechos de otros usuarios y solo cuando esos usuarios tienen habilitado el acceso web.

Craig Ringer
fuente
1
Por cierto, es posible que desee ver cómo pgbouncerfunciona para algunos detalles.
Craig Ringer el
2
Ah, DISCARD ALLes otra forma de que los derechos vuelvan a sus valores predeterminados. Realmente desearía que Pg tuviera una SET ROLE NORESETo similar ...
Craig Ringer