¿Deberían las restricciones de seguridad hacer que un servicio devuelva nulo o arroje una excepción? [cerrado]

11

Estoy un poco en desacuerdo con un desarrollador más experimentado en este tema, y ​​me pregunto qué piensan los demás al respecto; nuestro entorno es Java, EJB 3, servicios, etc.

El código que escribí llama a un servicio para obtener cosas y crear cosas. El problema con el que me encontré fue que obtuve excepciones de puntero nulo que no tenían sentido. Por ejemplo, cuando le pido al servicio que cree un objeto, me devuelve el valor nulo; cuando intento buscar un objeto con una identificación válida conocida, me devuelve el valor nulo. Pasé algún tiempo tratando de descubrir qué estaba mal en mi código; Como tengo menos experiencia, por lo general asumo que he hecho algo mal, pero resulta que la razón de los retornos nulos fue la seguridad. Si el usuario principal que usa mi servicio no tenía los permisos correctos para el servicio de destino, entonces simplemente devuelve nulo. La mayoría de los otros servicios aquí tampoco están bien documentados, por lo que aparentemente esto es algo que debe saber.

Esto es bastante confuso como un desarrollador que escribe código que interactúa con el servicio. Tendría mucho más sentido para mí si el servicio fuera una excepción que me dijera que el usuario no tenía los permisos adecuados para cargar esta cosa o crearla; Entonces sabría de inmediato por qué mi servicio no funcionaba como se esperaba.

El desarrollador más experimentado que escribió el servicio argumentó que pedir los datos no es una condición de error, y que las excepciones solo deben generarse en una condición de error, no cuando el usuario no tiene acceso a los datos. Estos datos a menudo se buscan en una GUI, y para aquellos usuarios sin los permisos correctos, estas cosas simplemente "no existen". En resumen: preguntar no está mal, por lo tanto, no hay excepción. Obtener métodos devuelve nulo porque para esos usuarios esas cosas "no existen". Los métodos de creación devuelven nulo cuando al usuario no se le permitió crear esa cosa.

¿Es esto normal y / o buena práctica? Prefiero usar excepciones porque me resulta mucho más fácil saber qué está pasando. Entonces, por ejemplo, también preferiría lanzar una NotFoundException si solicitó un objeto con una ID no válida, en lugar de devolver nulo.

Svish
fuente
@ChrisF: 1) trata sobre personas que evitan referencias nulas, lo cual no hago. En muchos casos son apropiados, simplemente no creo que estén en este. 2) se trata de verificar los parámetros, y ¿no sería una afirmación de resultado lo mismo que arrojar una excepción? 3) las excepciones frente a los códigos de error también son un problema diferente, ya que son dos formas de "mostrar" lo que salió mal. Los códigos de error son apropiados si, por ejemplo, necesita notificar a un sistema que no admite excepciones o si no desea que el usuario final vea algo más que un código.
Svish
El tema que pregunto aquí es sobre "pretender que algo no existe o no sucedió" versus "decir por qué algo no existe o no sucedió".
Svish
3
No sugerí que fueran duplicados, solo que podrían tener información útil para usted.
ChrisF
Es cierto, lo siento por eso! Lo tomó como un comentario de "posibles duplicados" por alguna razón ... probablemente por falta de sueño, jeje.
Svish

Respuestas:

19

Solo se deben lanzar excepciones cuando hay un error y pedir algo no es un error.

Pedir algo puede no ser un error, pero no tener permisos para algo que solicitó es seguramente algún tipo de error. Las excepciones son una forma alternativa de informar condiciones excepcionales, que se utilizarán en lugar de valores de retorno especiales (como null, como usted escribió, no es de ninguna ayuda si hay> 1 posibles razones por las cuales las cosas podrían salir mal (incluso si hay exactamente 1 posible razón ahora, podría haber 2 posibles razones en la próxima versión, por lo que usarlo nullcomo un valor de retorno que indica una falla sería pintarlo en la esquina)). Si el servicio no informa de ninguna manera por qué no hará lo que pediste, entonces definitivamente es un mal diseño.

Si se debe usar un valor de retorno especial (por ejemplo, los enteros negativos son útiles para funciones que normalmente devuelven un entero no negativo), una excepción, un controlador de error global o un registrador, etc., es un detalle de implementación al final. La elección depende del idioma, de la situación, de las convenciones, y también es una cuestión de gustos. El punto principal es que debería haber alguna forma directa de descubrir por qué algo no funciona. De lo contrario, su única opción es buscar basura con diferentes opciones y lo que sea para averiguar qué se correlaciona con el comportamiento de la caja negra, y es una pérdida de tiempo.

String.substring()arroja un IndexOutOfBoundsExceptionsi el índice está fuera de los límites. No puedo pensar en ninguna ventaja de regresar null, aunque, filosóficamente, se podría argumentar que a Stringno contiene nada fuera de sus límites, por nulllo que sería un valor de retorno lógico. Ser lógico-filosófico y ser práctico son obviamente dos animales diferentes.

Joonas Pulakka
fuente
Si substring () va a devolver un valor para índices fuera de los límites, debería devolver la parte de la cadena entre los índices, posiblemente vacía. Eso podría guardar una prueba en algún código de llamada.
Kevin Cline
2
Sí, existe este "valor vacío" interminable versus discusión nula: ¿la subcadena para los índices fuera de límite debe ser nula o una cadena vacía? No sé, ambos son efectivamente "nada" (bueno, ¿tal vez nulo es "más nada" que una cadena vacía?), Pero ninguno da la cantidad de información que hace una excepción.
Joonas Pulakka
@kevincline: A menos que uno esté usando un marco donde una referencia nula es un medio idiomático de indicar una cadena vacía, no veo ninguna base para substringdevolver una referencia nula. En mi humilde opinión, debería haber dos funciones separadas, una de las cuales promete devolver el número solicitado de caracteres o lanzar una excepción, y una de las cuales devuelve tanto como sea posible. Cada método sería sustancialmente superior al otro en algunos contextos.
supercat
@supercat: No dije nada sobre devolver nulo. Dije 'regresar ... la parte ... posiblemente vacía'. Una cadena vacía no es nula, excepto para Oracle.
Kevin Cline
@JoonasPulakka: Debería haberte fijado en mi comentario anterior.
supercat
5

Todo se reduce a lo que es el contrato. Si su contrato dice que puede solicitar algo que no existe, debe decir qué sucede (nulo u objeto nulo).

Por otro lado, si su contrato dice que primero debe llamar a un método diferente ( DoesSomethingExist()) y luego llamar al Getmétodo, entonces su contrato podría decir que solo puede obtener cosas que existen y lanzar una excepción si no lo hacen. El mensaje de excepción podría decir algo como "Asegúrese de llamar DoesSomethingExist()primero", que es un mensaje de error útil.

Scott Whitlock
fuente
En este caso, diría que no es normal pedir algo que no existe, ya que probablemente no pediría una identificación que no existe. Por lo general, primero habría obtenido una lista de cosas y luego buscaría una en particular para obtener más detalles. Entonces, si hubiera un findAllThingsmétodo, entonces diría que es apropiado devolver una lista vacía o un subconjunto si hay algunas o todas las cosas que no puede ver. De la misma manera, en un blog solo puedo enumerar publicaciones para editar pertenecientes al usuario actual. Pero si ese usuario trató de modificar la URL y editar una publicación diferente ...
Svish
4

No hay una única respuesta única para la pregunta. El uso de excepciones o devolver nulo depende de la situación.

En lugar de ver esto puramente desde un punto de vista dogmático, mira la interfaz como una interfaz de usuario . Las interfaces de usuario deben ser utilizables . Entonces, independientemente de sus propias opiniones sobre la forma "correcta" de hacer algo, elija el método que sea más útil desde la perspectiva de alguien que use su interfaz.

Si la persona que llama necesita saber por qué no se devuelve un objeto, es apropiada una excepción. Sin embargo, si el concepto general es "si no tiene permiso, no existe", el valor nulo es aceptable.

Específicamente en su caso, yo diría que los nulos son perfectamente aceptables para buscar un artículo si primero se requiere que la persona que llama inicie sesión o se conecte al servidor. Es entonces cuando le dirían que tiene o no tiene permiso. Una vez que pasa la puerta, es razonable que cuando busque algo que no tenga permiso para ver (o incluso sepa que existe), debería obtener un valor nulo.

Si, por otro lado, no tiene una conexión inicial o un paso de inicio de sesión, una excepción tiene sentido. Si la persona que llama sabe que existe un elemento y no lo está recuperando, la API no está siendo útil para devolver un valor nulo.

Sin embargo, para crear un artículo, esa es una historia diferente. Presumiblemente, podría haber muchas razones para que eso falle: sin permisos, parámetros incorrectos, servidor sin memoria, etc. Si al desarrollador se le da un valor nulo para todas esas condiciones, no tienen forma de determinar un curso de acción adecuado .

Entonces, para responder la pregunta, pregúntese cuál es la solución más útil desde la perspectiva de alguien que usa el servicio. Puede ser que la forma en que lo esté usando sea atípica, y para el caso más común, un nulo es la respuesta correcta.

Por lo tanto, no entre en una guerra religiosa por esto, decida qué es lo correcto para este problema en particular.

Bryan Oakley
fuente
No planeo ponerme equipo de batalla por esto, jeje. Tenía curiosidad por saber si estaba completamente equivocado al pensar lo que estaba pensando o no. Quiero aprender, pero no me gusta saltar a algo solo porque una persona lo dice. Y especialmente no si va en contra de mi propia experiencia y lógica (no importa cuán pequeño). De todos modos, estoy totalmente de acuerdo en que si se tratara de una interfaz de usuario final, eso podría tener sentido. Sin embargo, ya que este es un grano que, por lo que sé, sólo se puede acceder por código, yo diría que, como desarrollador, es el usuario.
Svish
1
Y como desarrollador, me importa por qué algo está mal, independientemente de lo que el usuario final deba saber al respecto.
Svish
Totalmente de acuerdo con Bryan. Incluso los desarrolladores son usuarios, es decir, de consumir algunos otros códigos de desarrolladores. Las decisiones de lanzamiento o nulo son un tipo de interfaz / interfaz gráfica de usuario que debería ser útil para la tarea específica. No solo por el término general de lógica o filosofía.
Independiente
3

Definitivamente creo que es un mal diseño . El puntero nulo no es una respuesta válida para mí y puede ser difícil de manejar.

Si el usuario intenta conectar un servicio sin la credencial adecuada, debería responder con una respuesta clara y concisa. La respuesta podría ser una excepción o un objeto especial pero no nulo.

Debe dejar la respuesta nula cuando el enlace de red está roto u otro mal funcionamiento crítico inesperado.

Además, el tiempo que pasa entendiendo por qué obtuvo un valor nulo es un argumento fuerte. Cualquier código debe ser fácil de entender y usar. Tener un valor nulo no es el caso.

Amina
fuente
Por su última oración, quiere decir " Debería devolver nulo solo cuando el enlace de red está roto, etc." o " Debe dejar de devolver nulo cuando el enlace de red está roto, etc." ?
Péter Török
@ Péter Török: no recomiendo el uso explícito de "return null;". Sin embargo, cuando ocurre una falla importante, es aceptable que algún servicio devuelva nulo.
Amine
¿No sería más apropiada una excepción si ocurriera un mal funcionamiento crítico inesperado?
Svish
2
@Amine: Diría que ese es el caso menos apropiado para el cual devolver nulo.
Michael Borgwardt
@Svish: si puede detectar el desglose mayor, por supuesto, una excepción será excelente.
Amine
3

Teniendo en cuenta un buen diseño de software, debe pensar en la vida de su proyecto.
Al regresar null, como se ha dicho, usted no da ninguna información al cliente. Mira lo que te pasó, al principio no te diste cuenta de dónde estaba el problema. Además, si no se proporciona documentación, esto es un desastre.

Al lanzar una excepción, puede saber qué salió mal. Incluso puede personalizar el texto que se muestra para que sea más específico si lo desea.

Por otro lado, al regresar null, hace que el cliente investigue lo que está sucediendo.

Además, se dio cuenta de que había un problema porque llegó a otra parte a NullPointerException. Ahora imagine que no almacena el retorno de esa función ... Se verá obligado a rodear cada llamada a esa función con un if elsebloque ...

Desde mi punto de vista, regresar nulles una mala práctica.

eversor
fuente
2

El desarrollador más experimentado que escribió el servicio argumentó que pedir los datos no es una condición de error, y que las excepciones solo deben generarse en una condición de error, no cuando el usuario no tiene acceso a los datos.

Desde mi punto de vista, esto no tiene sentido.

La consulta de datos sin permiso debería dar como resultado una excepción, porque es un resultado inesperado , para no obtener ningún resultado, sin el acceso adecuado.

Analogía : el código de respuesta para un GETsin autorización no es 200 oksin datos, en realidad es 401 Unauthorized.

Thomas Junk
fuente
1

Devolver nulo no es genial. No te dice nada. Para tomar su ejemplo, si una aplicación está tratando de buscar algo y la respuesta es nula para ambos problemas de seguridad y si el elemento no existe, entonces, ¿cómo puede saber la diferencia entre los dos?

Una respuesta significativa puede permitir que la aplicación tome decisiones lógicas sobre qué hacer a continuación o cómo resolver problemas.

Qwerky
fuente
Pregunta cómo diferencia la diferencia entre un objeto que no está allí y no tiene permiso para verlo. Quizás el diseño del sistema requiere que no lo sepas. No creo que tengamos suficiente información para responder en este caso específico, y no hay una respuesta que funcione en todos los casos. Hay casos de uso perfectamente válidos donde, si no puede verlo, efectivamente no está allí. Y, hay casos de uso en los que si no puede verlo, se le debe dar una excepción para decir por qué no puede verlo.
Bryan Oakley
¿Qué quieres decir con "el diseño del sistema requiere que no lo sepas"? ¿Qué tipo de diseño podría ser?
Svish
2
Si la política de seguridad dice que no puedes verla, entonces en la mayoría de los casos tampoco deberías saber que existe. Eso hace que regresar "no encontrado" sea perfectamente aceptable.
Blrfl
1

Si la causa raíz es un usuario no autenticado, entonces prefiero una respuesta HTTP 401 dura, tal vez con una redirección a una página de mensajes bonitos (y una notificación a alguien a quien le importe). Las causas relacionadas con la autorización son más caso por caso; existen argumentos para devolver errores HTTP (403, por ejemplo) y argumentos para devolver códigos / mensajes especiales bien formados en la respuesta del servicio.

Las excepciones solo deben usarse en circunstancias excepcionales y significan que algo ha salido mal. No estoy de acuerdo en que deberían estar sobrecargados para significar "no permitido". (Lo mismo es cierto para el valor de retorno nulo: no estoy de acuerdo con que deba usarse para significar "no permitido").

marca
fuente
Bueno, en la GUI podría hacer esto, pero en cuanto al bean que implementa el servicio que hace las cosas detrás de escena, solo tiene excepciones y valores de retorno especiales a su disposición. Por supuesto, no quiero decir que el usuario deba obtener la excepción, pero la excepción podría, por ejemplo, quedar atrapada en el servicio web / servlet / lo que sea y luego hacer lo que sea apropiado. Redireccionar a algún lugar, página http 4xx rígida, o algo más.
Svish
Buen punto, pero podría emplear un paradigma como AOP para capturar estos casos. El Aspecto podría probar el privilegio de llamar a una operación determinada y redirigir / permitir que el procesamiento continúe según corresponda.
Mark
0

Para hacer de abogado del diablo, intentaré ponerme del lado de volver nulo.

En mi opinión, solo hay una situación en la que este tipo de respuesta es casi aceptable y es, como en este caso, cuando se trata de la seguridad.

Es muy fácil regalar accidentalmente detalles de su sistema que las personas no autorizadas no deben conocer. Esto debe ser evitado.

Tome, por ejemplo, un verificador de nombre de usuario y contraseña. Devolver un error de "Nombre de usuario no encontrado" y un error de "Contraseña incorrecta" como códigos de error separados obviamente lo abriría a serios problemas de seguridad ya que alguien primero podría buscar un nombre de usuario válido y luego buscar una contraseña. Devolver un "nombre de usuario / contraseña no válido" genérico sería una mejor opción.

Por lo tanto, en este caso particular, yo diría que es casi bien para volver nullpero estoy de acuerdo, sería muy desagradable para trabajar.

OldCurmudgeon
fuente
Sí, no estaría de acuerdo. En mi opinión, la respuesta adecuada sería una excepción, pero la misma para el nombre de usuario no encontrado y la contraseña incorrecta. FailedToLogin, InvalidCredentials, lo que sea.
Svish
@Svish - Pero si está tratando de no dar pistas sobre el motivo de la falla, entonces regresar nulles exactamente la respuesta menos útil. Casi cualquier otra cosa podría utilizarse para descubrir algo sobre el sistema. La razón por la que OP se quejó fue porque la nulldevolución no solo explicaba nada, sino que incluso no era útil. Ese es el objetivo deliberado ... no decir absolutamente nada y ser inútil. Misión cumplida en mi opinión.
OldCurmudgeon
Bueno, eso podría ser así, pero aún así diría que hacerlo es malo. Al menos dentro de un sistema. En los bordes donde el usuario final ve las cosas, entonces seguro, puedo estar de acuerdo. Pero dentro del sistema, los desarrolladores somos los usuarios, y ¿por qué querríamos hacer las cosas más confusas para nosotros? Como si el desarrollo de software no fuera lo suficientemente difícil ...
Svish
1
Y con el ejemplo de inicio de sesión, diría que es malo que la experiencia del usuario no diga al menos algo acerca de por qué no pareció ocurrir nada cuando (pensaron que) ingresaron sus credenciales. Si la página se actualiza y parece que simplemente ignoró mi intento de iniciar sesión, asumiré que algo está mal con el sistema, no es que lo que ingresé esté mal.
Svish
-2

Creo que return null no es amigable, cuando el cliente invoca este método y devuelve null, el cliente no sabe qué sucedió y por qué regresó nulo. Por lo tanto, deberíamos lanzar la ejecución, que tiene sentido y proporcionar una documentación para describir esta ejecución.

Mark xie
fuente