Lo que se entiende por "un usuario no debe decidir si es un administrador o no. Los privilegios o el sistema de seguridad deberían ".


55

El ejemplo utilizado en la pregunta pasa datos mínimos básicos a una función que toca la mejor manera de determinar si el usuario es administrador o no. Una respuesta común fue:

user.isAdmin()

Esto provocó un comentario que se repitió varias veces y se votó muchas veces:

Un usuario no debe decidir si es un administrador o no. El sistema de privilegios o seguridad debería. Algo que esté estrechamente vinculado a una clase no significa que sea una buena idea hacer que forme parte de esa clase.

Respondí,

El usuario no está decidiendo nada. El objeto / tabla Usuario almacena datos sobre cada usuario. Los usuarios reales no pueden cambiar todo sobre ellos mismos.

Pero esto no fue productivo. Claramente, existe una diferencia de perspectiva subyacente que dificulta la comunicación. ¿Alguien puede explicarme por qué user.isAdmin () es malo y pintar un breve boceto de lo que parece hecho "bien"?

Realmente, no veo la ventaja de separar la seguridad del sistema que protege. Cualquier texto de seguridad dirá que la seguridad debe diseñarse en un sistema desde el principio y considerarse en cada etapa del desarrollo, implementación, mantenimiento e incluso el final de la vida útil. No es algo que se pueda atornillar a un lado. Pero 17 votos positivos hasta ahora en este comentario dicen que me falta algo importante.


8
No creo que sea malo, especialmente si es solo una bandera en la tabla db del usuario. Si hubiera cambios en el futuro, cámbielo en el futuro. También puede llamar al sistema de seguridad desde el objeto de usuario. Una vez leí sobre el patrón de diseño de que cada acceso db / resource debería pasar por el objeto de usuario actual, para asegurarme de que nunca intentes hacer algo que no está permitido. Podría ser un poco demasiado, pero el usuario podría proporcionar un objeto de "privilegios" que cubra todas las cosas relacionadas con la seguridad.
Cefalópodo

Creo que hubo un malentendido. Para usted, "Usuario" significa la persona que utiliza el software. Para ellos, "Usuario" significa el código que utiliza su funcionalidad.
Philipp

2
No es que lo hayas preguntado, pero lo que más tendré cuidado en este caso es un método IsAdmin (), donde sea que lo pongas. Por lo general, recomendaría que un "Administrador" no esté codificado, pero solo tiene un conjunto de permisos posibles y un "Administrador" tiene todo el conjunto. De esa manera, cuando necesite dividir la función de administrador más tarde, las cosas son mucho más fáciles. Y reduce la lógica de casos especiales.
psr

1
@Philipp No lo creo ... Ambos sitios están hablando explícitamente de un Userobjeto con un isAdmin()método (lo que sea que haga detrás de escena)
Izkata

Demasiado tarde para editar;
Quise

Respuestas:


12

Piense en el Usuario como una clave y en los permisos como un valor.

Diferentes contextos pueden tener diferentes permisos para cada usuario. Como los permisos son relevantes para el contexto, deberá cambiar el usuario para cada contexto. Por lo tanto, es mejor que coloque esa lógica en otro lugar (por ejemplo, la clase de contexto) y simplemente busque los permisos donde sea necesario.

Un efecto secundario es que puede hacer que su sistema sea más seguro, porque no puede olvidar borrar la bandera de administrador para los lugares donde no es relevante.


40

Ese comentario estaba mal redactado.

El punto no es si el objeto de usuario "decide" si es un administrador, un usuario de prueba, un superusuario o cualquier cosa dentro o fuera de lo común. Obviamente, no decide ninguna de esas cosas, el sistema de software en general, tal como lo implementa usted, lo hace. El punto es si la clase "usuario" debe representar los roles del principal que representan sus objetos, o si esto es responsabilidad de otra clase.


66
Ay (fue mi comentario), pero lo pones mucho mejor que yo.
Marjan Venema el

¿Está diciendo que modele roles en una clase de rol separada, no en la clase de usuario? ¿No está recomendando un sistema separado como LDAP? ¿Qué sucede si cada decisión que toma la clase Role requiere consultar el registro de usuario apropiado y no requiere otra información? ¿Debería estar separado del usuario y, de ser así, por qué?
GlenPeterson el

2
@GlenPeterson: no necesariamente, solo las tablas de clases <> y, por lo general, hay muchas más clases para discernir que las tablas. Sin embargo, el hecho de estar orientado a los datos lleva a definir sublistas en las clases, cuando muchas veces estas listas deben definirse en otro "libro mayor" (órdenes) o alguna clase de ese tipo que pase al usuario (o la orden) para recuperar ... Es más una cuestión de responsabilidades que de verbos y sustantivos.
Marjan Venema el

99
Piense en el Usuario como una clave y en los permisos como un valor. Diferentes contextos pueden tener diferentes permisos para cada usuario. Como los permisos son relevantes para el contexto, deberá cambiar el usuario para cada contexto. Por lo tanto, es mejor que ponga esa lógica en otro lugar (por ejemplo, la clase de contexto).
Wilbert el

2
@Wilbert: ¡Contexto! ¡Eso es particularmente esclarecedor! Sí, si tiene más de un contexto para esos permisos, entonces todo tiene sentido para mí. Por lo general, veo estos permisos en un solo contexto, y pegarlos en el usuario es la solución más simple en ese caso. Claramente, no pertenecen allí si se aplican de manera diferente en un segundo contexto. Si escribe eso como respuesta, es probable que lo acepte. Gracias.
GlenPeterson el

30

No hubiera elegido redactar el comentario original de la forma en que fue redactado, pero identifica un problema potencialmente legítimo.

Específicamente, las preocupaciones que justifican la separación son autenticación versus autorización .

La autenticación se refiere al proceso de iniciar sesión y obtener una identidad. Es la forma en que los sistemas saben quién es usted y cómo se utilizan para cosas como personalización, propiedad de objetos, etc.

La autorización se refiere a lo que tiene permitido hacer , y esto (generalmente) no está determinado por quién es usted . En cambio, está determinado por alguna política de seguridad como roles o permisos, que no se preocupan por cosas como su nombre o dirección de correo electrónico.

Estos dos pueden cambiar ortogonalmente entre sí. Por ejemplo, puede cambiar el modelo de autenticación agregando proveedores OpenID / OpenAuth. Y puede cambiar la política de seguridad agregando un nuevo rol o cambiando de RBAC a ABAC.

Si todo esto entra en una clase o abstracción, entonces su código de seguridad, que es una de sus herramientas más importantes para la mitigación de riesgos , se convierte, irónicamente, en de alto riesgo.

He trabajado con sistemas en los que la autenticación y la autorización estaban demasiado unidas. En un sistema, había dos bases de datos de usuarios paralelas, cada una para un tipo de "rol". Aparentemente, la persona o el equipo que lo diseñó nunca consideró que un solo usuario físico podría tener ambos roles, o que podría haber ciertas acciones que eran comunes a múltiples roles, o que podría haber problemas con las colisiones de ID de usuario. Este es un ejemplo ciertamente extremo, pero fue / es increíblemente doloroso trabajar con él.

Microsoft y Sun / Oracle (Java) se refieren al conjunto de información de autenticación y autorización como Principal de seguridad . No es perfecto, pero funciona razonablemente bien. En .NET, por ejemplo, tiene IPrincipal, que encapsula el IIdentity- el primero es un objeto de política (autorización) mientras que el segundo es una identidad (autenticación). Podría cuestionar razonablemente la decisión de colocar uno dentro del otro, pero lo importante es que la mayoría del código que escriba será solo para una de las abstracciones, lo que significa que es fácil de probar y refactorizar.

No hay nada de malo en un User.IsAdmincampo ... a menos que también haya un User.Namecampo. Esto indicaría que el concepto de "Usuario" no está bien definido y este es, lamentablemente, un error muy común entre los desarrolladores que están un poco mojados detrás de las orejas cuando se trata de seguridad. Por lo general, lo único que debe compartir la identidad y la política es la ID de usuario, que, no por coincidencia, es exactamente cómo se implementa en los modelos de seguridad de Windows y * nix.

Es completamente aceptable crear objetos de contenedor que encapsulen tanto la identidad como la política. Por ejemplo, facilitaría la creación de una pantalla de tablero en la que necesita mostrar un mensaje de "saludo" además de varios widgets o enlaces a los que el usuario actual tiene permitido acceder. Siempre que este contenedor solo envuelva la información de identidad y política, y no afirme ser el propietario. En otras palabras, siempre que no se presente como una raíz agregada .

Un modelo de seguridad simplista siempre parece una buena idea cuando diseñas una nueva aplicación por primera vez, debido a YAGNI y todo eso, pero casi siempre termina volviendo a morderte más tarde, porque, sorpresa sorpresa, ¡se agregan nuevas funciones!

Entonces, si sabe lo que es mejor para usted, mantendrá la información de autenticación y autorización por separado. Incluso si la "autorización" en este momento es tan simple como un indicador "IsAdmin", aún así será mejor si no es parte de la misma clase o tabla que la información de autenticación, de modo que si y cuando su política de seguridad necesite cambie, no necesita realizar una cirugía reconstructiva en sus sistemas de autenticación que ya funciona bien.


11
Oh, desearía haber tenido tiempo para escribir esto. Por otra parte, probablemente no habría podido decirlo tan bien como lo hizo sin pensarlo mucho más de mi parte. Muchas veces mi instinto me dice que vaya de una manera u otra, pero explicar por qué, para mí o para otra persona, requiere mucho más pensamiento y esfuerzo. Gracias por este post Lo habría multiplicado, pero SE no me deja ...
Marjan Venema

Esto suena sorprendentemente similar a un proyecto en el que estoy trabajando y su respuesta me dio otra perspectiva. Muchas gracias!
Brandon el

"No hay nada de malo en un campo User.IsAdmin ... a menos que también haya un campo User.Name" que escriba. ¿Pero qué isAdmines un método? Podría simplemente delegar en un objeto cuya responsabilidad es realizar un seguimiento de los permisos. Lo que es mejor práctica en el diseño de un modelo relacional (qué campos tiene una entidad) no es necesariamente traducible a lo que es mejor práctica en un modelo OO (a qué mensajes puede responder un objeto).
KaptajnKold

@KaptajnKold: Sigo leyendo este sentimiento en varios hilos de preguntas y respuestas en este foro. Si existe un método, no importa si realiza directamente las operaciones o si las delega , todavía cuenta como una responsabilidad porque otras clases pueden confiar en él . Es User.IsAdminmuy posible que sea una línea que no hace nada más que llamar PermissionManager.IsAdmin(this.Id), pero si otras 30 clases hacen referencia a ella, no puede cambiarla ni eliminarla. Por eso existe el SRP.
Aaronaught

Y, @KaptajnKold, en lo que respecta a los modelos relacionales, no estoy seguro de a qué te refieres; Es aún peor tener un IsAdmin campo . Es el tipo de campo que tiende a permanecer mucho tiempo después de que el sistema se ha convertido en RBAC o algo más complejo, y gradualmente se desincroniza con los permisos "reales", y las personas olvidan que ya no deben usarlo, y ... bueno, solo di que no. Incluso si cree que solo tiene dos roles, "admin" y "no admin", no lo almacene como un campo booleano / bit, normalice correctamente y tenga una tabla de permisos.
Aaronaught

28

Bueno, al menos viola el principio de responsabilidad única . ¿Debería haber cambios (múltiples niveles de administración, incluso más roles diferentes?) Este tipo de diseño sería bastante desordenado y horrible de mantener.

Considere la ventaja de separar el sistema de seguridad de la clase de usuario, para que ambos puedan tener un rol único y bien definido. Terminas con un diseño más limpio y fácil de mantener en general.


2
@GlenPeterson: No, los usuarios pueden existir sin seguridad. ¿Qué pasa con mi nombre, mi nombre para mostrar, mi dirección, mi dirección de correo electrónico, etc. ¿Dónde los pondría? La identificación (autenticación) y la autorización son diferentes bestias por completo.
Marjan Venema el

2
class Useres un componente central en el triplete de seguridad de Identificación, Autenticación, Autorización . Estos están íntimamente vinculados a nivel de diseño, por lo que no es irrazonable que el código refleje esta interdependencia.
MSalters el

1
La pregunta es, ¿debería la clase Usuario contener toda la lógica para manejar el triplete!
Zavior

3
@MSalters: si es una API para dicha lógica, lo sabe incluso si delega en otro lugar para la lógica exacta. El punto que se destaca es que la Autorización (permisos) se trata de una combinación de un usuario con otra cosa y, por lo tanto, no debe ser parte del usuario o de otra cosa, sino parte de los sistemas de permisos que tienen plena conciencia de ambos usuarios y lo que sea se le está dando permiso para.
Marjan Venema el

2
Should there be changes- Bueno, no sabemos si habrá cambios. YAGNI y todo. ¿Esos cambios cuando es necesario, verdad?
Izkata

10

Creo que el problema, tal vez mal redactado, es que pone demasiado peso en la Userclase. No, no hay ningún problema de seguridad con tener un método User.isAdmin(), como se sugirió en esos comentarios. Después de todo, si alguien es lo suficientemente profundo en su código para inyectar código malicioso para una de sus clases principales, tiene serios problemas. Por otro lado, no tiene sentido Userdecidir si es un administrador para cada contexto. Imagine que agrega diferentes roles: moderadores para su foro, editores que pueden publicar publicaciones en la página principal, escritores que pueden escribir contenido para que los editores publiquen, etc. Con el enfoque de ponerlo en el Userobjeto, terminarás con demasiados métodos y demasiada lógica viviendo en Usereso que no está directamente relacionado con la clase.

En su lugar, podría usar una enumeración de diferentes tipos de permisos y una PermissionsManagerclase. Quizás podría tener un método como PermissionsManager.userHasPermission(User, Permission)devolver un valor booleano. En la práctica, podría verse así (en Java):

if (!PermissionsManager.userHasPermission(user, Permissions.EDITOR)) {
  Site.renderSecurityRejectionPage();
}

Es esencialmente equivalente al método del before_filtermétodo Rails , que proporciona un ejemplo de este tipo de verificación de permisos en el mundo real.


66
¿Cómo es eso diferente de user.hasPermission(Permissions.EDITOR), excepto que es más detallado y procesal en lugar de OO?
Cefalópodo

99
@Arian: porque user.hasPermissionspone demasiadas responsabilidades en la clase de usuario. Requiere que el usuario conozca los permisos, mientras que un usuario (clase) debería poder existir sin dicho conocimiento. Tampoco desea que una clase de usuario sepa cómo imprimir o mostrar (crear un formulario), lo deja a un procesador / generador de impresoras o muestra una clase de procesador / generador.
Marjan Venema el

44
user.hasPermission(P)es la API correcta, pero esa es solo la interfaz. Por supuesto, la decisión real debe tomarse en un subsistema de seguridad. Para abordar el comentario de syrion sobre las pruebas, durante las pruebas puede cambiar ese subsistema. Sin embargo, el beneficio de lo directo user.hasPermission(P)es que no contamina todo el código solo para poder probarlo class User.
MSalters el

3
@MarjanVenema Según esa lógica, el usuario seguiría siendo un objeto de datos sin ninguna responsabilidad. No estoy diciendo que toda la gestión de permisos deba implementarse en la clase de usuario, si es necesario un sistema tan complejo. Usted dice que un usuario no necesita saber acerca de los permisos, pero no veo por qué todas las demás clases necesitan saber cómo resolver los permisos de un usuario. Esto hace que las burlas y las pruebas sean mucho más difíciles.
Cefalópodo

66
@Arian: mientras que las clases anémicas son un olor, algunas clases simplemente no tienen ningún comportamiento ... El usuario, en mi opinión, es una de esas clases que se usa mejor como parámetro para un montón de otras cosas (que quieren hacer cosas / tomar decisiones basadas en quién las solicita), en lugar de ser utilizado como un contenedor para todo tipo de comportamiento solo porque parece conveniente codificarlo de esa manera.
Marjan Venema el

9

Object.isX () se usa para representar una relación clara entre el objeto y X, que se puede expresar como un resultado booleano, por ejemplo:

Water.isBoiling ()

Circuit.isOpen ()

User.isAdmin () asume un significado único de 'Administrador' dentro de cada contexto del sistema , que un usuario es, en cada parte del sistema, un administrador.

Si bien parece bastante simple, un programa del mundo real casi nunca se ajustará a este modelo, sin duda habrá un requisito para que un usuario administre el recurso [X] pero no [Y], o para tipos más distintos de administradores (un [proyecto ] administrador frente a un [sistema] administrador).

Esta situación generalmente requiere una investigación de los requisitos. En raras ocasiones, si alguna vez, un cliente querrá la relación que User.isAdmin () realmente representa, por lo que dudaría en implementar cualquier solución sin aclaración.


6

El póster simplemente tenía un diseño diferente y más complicado en mente. En mi opinión, User.isAdmin()está bien . Incluso si luego presenta algún modelo de permisos complicado, veo pocas razones para moverme User.isAdmin(). Más adelante, es posible que desee dividir la clase Usuario en un objeto que represente un usuario conectado y un objeto estático que represente los datos sobre el usuario. O tal vez no. Ahorra mañana para mañana.


44
Save tomorrow for tomorrow. Sí. Cuando descubres que el código estrechamente acoplado que acabas de escribir te ha encasillado y tienes que volver a escribirlo porque no te tomaste 20 minutos adicionales para desacoplarlo ...
MirroredFate

55
@MirroredFate: es completamente posible tener un User.isAdminmétodo conectado a un mecanismo de permiso arbitrario sin acoplar la clase de usuario a ese mecanismo específico.
Kevin Cline

3
Para que User.isAdmin se acople a un mecanismo de permisos arbitrario, incluso sin acoplamiento a un mecanismo específico, se requiere ... bueno ... acoplamiento. El mínimo de los cuales sería a una interfaz. Y esa interfaz simplemente no debería estar allí. Le está dando a la clase de usuario (e interfaz) algo de lo que simplemente no debería ser responsable. Aaronaught lo explica mucho mejor que yo (al igual que la combinación de todas las otras respuestas a esta pregunta).
Marjan Venema el

8
@MirroredFate: no me importa si tengo que reescribir una implementación simple para cumplir requisitos más complejos. Pero he tenido experiencias realmente malas con API demasiado complejas diseñadas para cumplir requisitos futuros imaginados que nunca surgieron.
Kevin Cline

3
@kevincline Si bien las API demasiado complejas son (por definición) algo malo, no estaba sugiriendo que se escribieran API demasiado complejas. Estaba diciendo que Save tomorrow for tomorrowes un enfoque de diseño terrible porque hace que los problemas que serían triviales si el sistema se implementara correctamente fueran mucho peores. De hecho, esa mentalidad es lo que da como resultado API demasiado complejas, ya que se agrega capa sobre capa para parchear el diseño inicial pobre. Supongo que mi postura es measure twice, cut once.
MirroredFate

5

Realmente no es un problema ...

No veo un problema con User.isAdmin(). Ciertamente lo prefiero a algo así PermissionManager.userHasPermission(user, permissions.ADMIN), que en el santo nombre de SRP hace que el código sea menos claro y no agrega nada de valor.

Creo que SRP está siendo interpretado un poco literalmente por algunos. Creo que está bien, incluso es preferible que una clase tenga una interfaz rica. SRP solo significa que un objeto debe delegar a los colaboradores cualquier cosa que no esté dentro de su responsabilidad. Si la función de administrador de un usuario es algo más complicado que un campo booleano, podría tener sentido que el objeto del usuario delegue en un Administrador de permisos. Pero eso no significa que tampoco sea una buena idea que un usuario conserve su isAdminmétodo. De hecho, significa que cuando su aplicación se gradúa de simple a compleja, debe cambiar el código que usa el objeto Usuario. IOW, su cliente no necesita saber qué se necesita realmente para responder a la pregunta de si un usuario es o no administrador.

... pero ¿por qué realmente quieres saber?

Dicho esto, me parece que rara vez necesita saber si un Usuario es un administrador, excepto para poder responder alguna otra pregunta, como si un usuario puede o no realizar alguna acción específica, por ejemplo, como actualizar un Widget. Si ese es el caso, preferiría tener un método en Widget, como isUpdatableBy(User user):

boolean isUpdatableBy(User user) {
    return user.isAdmin();
}

De esa manera, un widget es responsable de saber qué criterios deben cumplirse para que un usuario lo actualice, y un usuario es responsable de saber si es un administrador. Este diseño se comunica claramente sobre sus intenciones y hace que sea más fácil graduarse a una lógica empresarial más compleja si llega el momento.

[Editar]

El problema con no tener User.isAdmin()y usarPermissionManager.userHasPermission(...)

Pensé en agregar a mi respuesta para explicar por qué prefiero llamar a un método en el objeto Usuario que llamar a un método en un objeto PermissionManager si quiero saber si un usuario es administrador (o tiene un rol de administrador).

Creo que es justo suponer que siempre dependerá de la clase de usuario donde sea que necesite hacer la pregunta: ¿ es este usuario administrador? Esa es una dependencia de la que no puedes deshacerte. Pero si necesita pasar al usuario a un objeto diferente para hacer una pregunta al respecto, eso crea una nueva dependencia de ese objeto además del que ya tiene en el Usuario. Si la pregunta se hace mucho, son muchos los lugares donde se crea una dependencia adicional, y son muchos los lugares que puede tener que cambiar si cualquiera de las dependencias lo exige.

Compare esto con mover la dependencia a la clase Usuario. Ahora, de repente, tiene un sistema en el que el código del cliente (el código que debe hacer la pregunta de si este usuario es un administrador ) no está acoplado a la implementación de cómo se responde esta pregunta. Puede cambiar completamente el sistema de permisos y solo tiene que actualizar un método en una clase para hacerlo. Todo el código del cliente permanece igual.

Insistir en no tener un isAdminmétodo en el Usuario por temor a crear una dependencia en la clase Usuario en el subsistema de permisos, es en mi opinión similar a gastar un dólar para ganar un centavo. Claro, evitas una dependencia en la clase Usuario, pero a costa de crear una en cada lugar donde necesites hacer la pregunta. No es una buena ganga.


2
"SRP solo significa que un objeto debe delegar a los colaboradores cualquier cosa que no esté dentro de su responsabilidad" - falso . La definición del SRP abarca todo lo que un objeto hace directamente y todo lo que delega. Una clase que solo realiza una tarea directamente, pero realiza otras 50 tareas indirectamente a través de la delegación, todavía (probablemente) está violando el SRP.
Aaronaught

KaptajnKold: I + 1 porque estoy de acuerdo contigo y me siento un poco culpable por eso. Su publicación se suma a la discusión, pero no responde la pregunta.
GlenPeterson

@GlenPeterson Bueno, traté de abordar la parte de la pregunta que era "lo que parece hecho" bien "". Además: no debes sentirte culpable por estar de acuerdo conmigo :)
KaptajnKold

1
@JamesSnell Quizás. Si. Pero ese es un detalle de implementación que aún limitaría dentro del método isAdmin en User. De esa manera, su código de cliente no tiene que cambiar cuando la "administración" de un Usuario evoluciona de ser un campo booleano a algo más avanzado. Es posible que tenga muchos lugares donde necesita saber si un usuario es administrador y no desea cambiarlos cada vez que cambie su sistema de autorización.
KaptajnKold

1
@JamesSnell ¿Quizás nos entendemos mal? La pregunta de si un usuario es o no un administrador no es lo mismo que la pregunta de si un usuario puede o no realizar una acción específica . La respuesta a la primera pregunta siempre es independiente del contexto. La respuesta a la segunda depende mucho del contexto. Esto es lo que intenté abordar en la segunda mitad de mi respuesta original.
KaptajnKold

1

Esta discusión me recordó una publicación de blog de Eric Lippert, que me llamó la atención en una discusión similar sobre diseño de clase, seguridad y corrección.

¿Por qué se sellan tantas clases marco?

En particular, Eric plantea el punto:

4) seguro. El punto central del polimorfismo es que puedes pasar objetos que se parecen a animales pero que en realidad son jirafas. Hay posibles problemas de seguridad aquí.

Cada vez que implemente un método que tome una instancia de un tipo sin sellar, DEBE escribir ese método para que sea robusto frente a instancias potencialmente hostiles de ese tipo. No puede confiar en ningún tipo de invariante que sepa que es cierto para SUS implementaciones, porque alguna página web hostil podría subclasificar su implementación, anular los métodos virtuales para hacer cosas que alteren su lógica y pasarla. Cada vez que sella una clase , Puedo escribir métodos que usan esa clase con la confianza de que sé lo que hace esa clase.

Esto puede parecer una tangente, pero creo que encaja con el punto que otros carteles han planteado sobre el principio de responsabilidad única SÓLIDO . Considere la siguiente clase maliciosa como una razón para no implementar un método o propiedad.User.IsAdmin

public class MyUser : User
{

    public new boolean IsAdmin()
    {
        // You know it!
        return true;
    }

    // Anything else we can break today?

}

De acuerdo, es un poco exagerado: nadie sugirió hacer IsAmdminun método virtual, pero podría suceder si su arquitectura de usuario / rol terminara siendo extrañamente compleja. (O tal vez no. Considere public class Admin : User { ... }: tal clase podría tener exactamente ese código arriba). Poner un binario malicioso en su lugar no es un vector de ataque común y tiene mucho más potencial para el caos que una biblioteca de usuario oscura, una vez más, esto podría ser el error de escalada de privilegios que abre la puerta a las travesuras reales. Finalmente, si una instancia binaria u objeto malicioso se abrió paso en el tiempo de ejecución, no es mucho más difícil imaginar que el "Sistema de privilegios o seguridad" sea reemplazado de manera similar.

Pero tomando en serio el punto de Eric, si pones tu código de usuario en un tipo particular de sistema vulnerable, tal vez acabas de perder el juego.

Ah, y para ser exactos para el registro, estoy de acuerdo con el postulado en la pregunta: "Un usuario no debe decidir si es un administrador o no". Los privilegios o el sistema de seguridad deberían ". Un User.IsAdminmétodo es una mala idea si está ejecutando el código en el sistema, no tiene el 100% de control del código; en su lugar, debería hacerlo Permissions.IsAdmin(User user).


¿Eh? ¿Cómo es esto pertinente? No importa dónde coloque la política de seguridad, siempre podría subclasificarla con algo inseguro. No importa si ese algo es un Usuario, Principal, Política, PermissionSet o lo que sea. Es un problema totalmente no relacionado.
Aaronaught

Es un argumento de seguridad a favor de la responsabilidad única. Para responder directamente a su punto, el módulo de seguridad y permisos será una clase sellada, pero ciertos diseños pueden desear que la clase Usuario sea virtual y heredada. Es el último punto en el artículo del blog (¿por lo tanto, quizás el menos probable / importante?), Y tal vez estoy combinando un poco los problemas, pero dada la fuente, está claro que el polimorfismo puede ser una amenaza para la seguridad y, por lo tanto, limitar las responsabilidades de una clase / objeto dado es más seguro.
Patrick M

No estoy seguro de por qué dices eso. Muchos marcos de seguridad y permisos son altamente extensibles. La mayoría de los tipos principales de .NET relacionados con la seguridad (IPrincipal, IIdentity, ClaimSet, etc.) no solo no están sellados sino que, de hecho, son interfaces o clases abstractas para que pueda enchufar lo que desee. De lo que Eric está hablando es de que no querrías que algo como System.Stringser heredable porque todo tipo de código de marco crítico hace suposiciones muy específicas sobre cómo funciona. En la práctica, la mayoría del "código de usuario" (incluida la seguridad) es heredable para permitir la duplicación de pruebas.
Aaronaught

1
De todos modos, el polimorfismo no se menciona en ninguna parte de la pregunta, y si sigues el enlace del autor al lugar donde se hizo el comentario original, está claro que no se refería a la herencia o incluso a los invariantes de clase en general, se trataba de responsabilidades.
Aaronaught

Sé que no se mencionó, pero el principio de las responsabilidades de clase está directamente relacionado con el polimorfismo. Si no hay un IsAdminmétodo para anular / cooptar, no hay un agujero de seguridad potencial. La revisión de los métodos de las interfaces del sistema, IPrincipaly IIdentity, está claro que los diseñadores no están de acuerdo conmigo, y por eso me conceden el punto.
Patrick M

0

El problema aquí es:

if (user.isAdmin()) {
   doPriviledgedOp();
} else {
   // maybe we can anyway!
   doPriviledgedOp();
}

O bien ha configurado su seguridad correctamente, en cuyo caso el método privilegiado debe verificar si la persona que llama tiene suficiente autoridad, en cuyo caso la verificación es superflua. O no lo ha hecho y está confiando en una clase sin privilegios para tomar sus propias decisiones sobre seguridad.


44
¿Qué es una clase sin privilegios?
Brandon

1
Realmente no hay nada que pueda hacer para evitar que se escriba este tipo de código. No importa cómo diseñe su aplicación, en algún lugar habrá una verificación explícita como esta, y alguien podría escribirla como insegura. Incluso si su sistema de permisos es tan simple como decorar un método con un atributo de rol o permiso, alguien podría arrojar incorrectamente un permiso "público" o "todos". Así que realmente no veo cómo esto hace que un método sea User.IsAdmin específicamente un problema.
Aaronaught
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.