Les restrictions de sécurité doivent-elles entraîner la nullité d'un service ou lever une exception? [fermé]

11

Je suis un peu en désaccord avec un développeur plus expérimenté sur cette question et je me demande ce que les autres en pensent; notre environnement est Java, EJB 3, services, etc.

Le code que j'ai écrit appelle un service pour obtenir des choses et pour créer des choses. Le problème que j'ai rencontré est que j'ai obtenu des exceptions de pointeur nul qui n'avaient aucun sens. Par exemple, lorsque je demande au service de créer un objet, je récupère null; lorsque j'essaie de rechercher un objet avec un ID valide connu, j'obtiens null back. J'ai passé quelque temps à essayer de comprendre ce qui n'allait pas dans mon code; comme je suis moins expérimenté, je suppose généralement que j'ai fait quelque chose de mal, mais il s'avère que la raison des retours nuls était la sécurité. Si l'utilisateur principal utilisant mon service n'avait pas les bonnes autorisations pour le service cible, il renvoie simplement null. La plupart des autres services ici ne sont pas non plus très bien documentés, donc apparemment, c'est juste quelque chose que vous devez savoir.

C'est assez déroutant pour un développeur qui écrit du code qui interagit avec le service. Cela aurait beaucoup plus de sens pour moi si le service contenait une exception qui me dirait que l'utilisateur n'avait pas les autorisations appropriées pour charger cette chose ou pour créer cette chose; Je saurais alors immédiatement pourquoi mon service ne fonctionnait pas comme prévu.

Le développeur le plus expérimenté qui a écrit le service a fait valoir que demander les données n'est pas une condition d'erreur et que les exceptions ne devraient être levées que dans une condition d'erreur, pas lorsque l'utilisateur n'a pas accès aux données. Ces données sont souvent recherchées dans une interface graphique, et pour les utilisateurs sans les bonnes autorisations, ces choses «n'existent tout simplement pas». Donc, en bref: demander n'est pas faux, donc pas d'exception. Les méthodes get renvoient null car pour ces utilisateurs ces choses «n'existent pas». Les méthodes de création renvoient null lorsque l'utilisateur n'était pas autorisé à créer cette chose.

Est-ce une pratique normale et / ou bonne? Je préfère utiliser les exceptions parce que je trouve beaucoup plus facile de savoir ce qui se passe. Donc, je préfère par exemple également lancer une NotFoundException si vous avez demandé un objet avec un ID non valide, plutôt que de retourner null.

Svish
la source
@ChrisF: 1) concerne les personnes évitant les références nulles, ce que je ne fais pas. Dans de nombreux cas, ils sont appropriés, je ne pense pas qu'ils se trouvent dans celui-ci. 2) concerne la vérification des paramètres, et une sorte de résultat assert ne reviendrait-elle pas à lancer une exception? 3) Les exceptions par rapport aux codes d'erreur sont également un problème différent car ce sont les deux façons de "montrer" ce qui n'a pas fonctionné. Les codes d'erreur sont appropriés si, par exemple, vous devez avertir un système qui ne prend pas en charge les exceptions ou si vous ne voulez pas que l'utilisateur final voie autre chose qu'un code.
Svish
La question que je pose ici est de "faire semblant que quelque chose n'existe pas ou ne s'est pas produit" vs "dire pourquoi quelque chose n'existe pas ou ne s'est pas produit".
Svish
3
Je n'ai pas suggéré qu'il s'agissait de doublons - simplement qu'ils pourraient vous fournir des informations utiles.
ChrisF
C'est vrai, désolé pour ça! Je l'ai pris comme un commentaire sur les "doublons possibles" pour une raison quelconque ... probablement à cause du manque de sommeil, hehe.
Svish

Réponses:

19

Les exceptions ne doivent être levées qu'en cas d'erreur et demander une chose n'est pas une erreur.

Demander une chose n'est peut-être pas une erreur, mais ne pas avoir l'autorisation de quelque chose que vous avez demandé est sûrement une sorte d'erreur. Les exceptions sont un autre moyen de signaler des conditions exceptionnelles, à utiliser à la place de valeurs de retour spéciales (comme null, qui, comme vous l'avez écrit, ne sont d'aucune utilité s'il y a> 1 raisons possibles pour lesquelles les choses pourraient mal tourner (même s'il y a exactement 1 raison possible maintenant, il pourrait y avoir 2 raisons possibles dans la prochaine version, donc utiliser nullcomme valeur de retour indiquant l'échec serait vous peindre dans le coin)). Si le service ne rapporte en aucune façon pourquoi il ne fera pas ce que vous avez demandé, alors c'est définitivement une mauvaise conception.

L'utilisation ou non d'une valeur de retour spéciale (par exemple, les entiers négatifs sont utiles pour les fonctions qui renvoient normalement un entier non négatif), une exception, un gestionnaire d'erreurs global ou un enregistreur, etc., est un détail d'implémentation à la fin. Le choix dépend de la langue, de la situation, des conventions, et c'est aussi une question de goût. Le point principal est qu'il devrait y avoir une façon directe de savoir pourquoi quelque chose ne fonctionne pas. Sinon, votre seule option est de jeter les ordures avec différentes options et autres pour savoir ce qui est en corrélation avec le comportement de la boîte noire, et c'est une perte de temps.

String.substring()jette un IndexOutOfBoundsExceptionsi l'index est hors limites. Je ne peux penser à aucun avantage à retourner à la nullplace, même si - philosophiquement - on pourrait dire que a Stringne contient rien en dehors de ses limites, ce nullserait donc une valeur de retour logique. Être logico-philosophique et être pratique sont évidemment deux animaux différents.

Joonas Pulakka
la source
Si substring () va retourner une valeur pour les index hors limites, elle doit retourner la partie de la chaîne entre les index, éventuellement vide. Cela pourrait enregistrer un test dans du code appelant.
Kevin Cline
2
Oui, il y a cette "valeur vide" sans fin par rapport à la discussion nulle: la sous-chaîne pour les index hors limites doit-elle être nulle, ou une chaîne vide? Je ne sais pas, les deux ne sont effectivement "rien" (enfin, peut-être que null est "plus rien" qu'une chaîne vide?), Mais ni l'un ni l'autre ne donne la quantité d'informations qu'une exception fait.
Joonas Pulakka
@kevincline: À moins que l'on n'utilise un cadre où une référence nulle est un moyen idiomatique d'indiquer une chaîne vide, je ne vois aucune base pour substringretourner une référence nulle. À mon humble avis, il devrait y avoir deux fonctions distinctes - dont l'une promet de renvoyer le nombre de caractères demandé ou de lever une exception, et dont l'une renvoie autant que possible. Chaque méthode serait sensiblement supérieure à l'autre dans certains contextes.
supercat
@supercat: Je n'ai rien dit sur le retour de null. J'ai dit 'retour ... la partie ... peut-être vide'. Une chaîne vide n'est pas nulle, sauf pour Oracle.
kevin cline le
@JoonasPulakka: J'aurais dû vous cingler à mon commentaire précédent.
supercat
5

Cela revient à ce qu'est le contrat. Si votre contrat stipule que vous pouvez demander quelque chose qui n'existe pas, il doit indiquer ce qui se passe (null ou objet null).

D'un autre côté, si votre contrat stipule que vous devez d'abord appeler une méthode différente ( DoesSomethingExist()) puis la Getméthode, votre contrat peut dire que vous ne pouvez obtenir que les choses qui existent et lever une exception si elles ne le font pas. Le message d'exception pourrait dire quelque chose comme «Assurez-vous d'appeler en DoesSomethingExist()premier», qui est un message d'erreur utile.

Scott Whitlock
la source
Dans ce cas, je dirais qu'il n'est pas normal de demander quelque chose qui n'existe pas, car vous ne demanderiez probablement pas un identifiant qui n'existe pas. Habituellement, vous auriez d'abord obtenu une liste de choses, puis recherchez-en une en particulier pour plus de détails. Donc, s'il y avait une findAllThingsméthode, je dirais qu'il convient de renvoyer une liste vide ou un sous-ensemble s'il y a certaines ou toutes les choses que vous n'êtes pas autorisé à voir. De la même manière, je pourrais sur un blog ne lister que les articles à éditer appartenant à l'utilisateur actuel. Mais si cet utilisateur a ensuite essayé de modifier l'URL et de modifier un message différent ...
Svish
4

Il n'y a pas de réponse unique à la question. Que ce soit pour utiliser des exceptions ou retourner null dépend de la situation.

Au lieu de regarder cela purement d'un point de vue dogmatique, regardez l'interface comme une interface utilisateur . Les interfaces utilisateur doivent être utilisables . Donc, indépendamment de vos propres opinions sur la "bonne" façon de faire quelque chose, choisissez la méthode la plus utilisable du point de vue de quelqu'un qui utilise votre interface.

Si l'appelant a besoin de savoir pourquoi un objet n'est pas retourné, une exception est appropriée. Si, cependant, le concept général est "si vous n'avez pas d'autorisation, cela n'existe pas", la valeur null est acceptable.

Plus précisément dans votre cas, je dirais que les valeurs nulles sont parfaitement acceptables pour rechercher un élément si l'appelant doit d'abord se connecter ou se connecter au serveur. C'est à ce moment qu'on vous dirait que vous avez ou non la permission. Une fois que vous avez franchi la porte, il est raisonnable que lorsque vous recherchez quelque chose que vous n'avez pas la permission de voir (ou même que vous savez qu'il existe), vous devriez obtenir un null.

Si, en revanche, vous n'avez pas de connexion initiale ou d'étape de connexion, une exception est logique. Si l'appelant sait qu'un élément existe et qu'il ne le récupère pas, l'API n'est pas utile pour renvoyer simplement une valeur nulle.

Pour créer un élément, cependant, c'est une autre histoire. Vraisemblablement, il pourrait y avoir de nombreuses raisons à cela - pas d'autorisations, de mauvais paramètres, une mémoire insuffisante du serveur, etc. .

Donc, pour répondre à la question, demandez-vous quelle est la solution la plus utilisable du point de vue d'une personne utilisant le service. Il se peut que la façon dont vous l' utilisez soit atypique, et dans le cas le plus courant, une valeur nulle est la bonne réponse.

Alors, ne vous lancez pas dans une guerre religieuse à ce sujet, décidez ce qui convient à ce problème particulier.

Bryan Oakley
la source
Je n'ai pas l'intention de mettre de l'équipement de combat là-dessus, hehe. Était simplement curieux de savoir si j'avais complètement tort de penser à ce que je pensais ou non. Je veux apprendre, mais je n'aime pas sauter à quelque chose juste parce qu'une personne le dit. Et surtout pas si cela va à l'encontre de ma propre expérience et de ma logique (si petite soit-elle). Quoi qu'il en soit, je suis totalement d'accord pour dire que s'il s'agissait d'une interface utilisateur final, cela pourrait en effet avoir un sens. Cependant, étant donné que c'est un haricot qui, autant que je sache, ne peut être accessible par code, je dirais que moi, en tant que développeur, est l'utilisateur.
Svish
1
Et en tant que développeur, je me soucie de savoir pourquoi quelque chose ne va pas, indépendamment de ce que l'utilisateur final devrait savoir à ce sujet.
Svish
Entièrement d'accord avec Bryan. Même les développeurs sont des utilisateurs, c'est-à-dire de consommer du code d'autres développeurs. Les décisions de lancer ou nul est une sorte d'interface / interface graphique qui devrait être utile pour la tâche spécifique. Pas seulement par le terme général de logique ou de philosophie.
Indépendant
3

Je pense vraiment que c'est une mauvaise conception . Null-pointer n'est pas une réponse valable pour moi et cela peut être difficile à gérer.

Si l'utilisateur essaie de se connecter à un service sans les informations d'identification appropriées, je dois répondre avec une réponse claire et concise. La réponse pourrait être une exception ou un objet spécial mais pas null.

Vous devez laisser la réponse nulle lorsque la liaison réseau est rompue ou tout autre dysfonctionnement critique inattendu.

De plus, le temps que vous passez à comprendre pourquoi vous avez obtenu un nul est un argument fort. Tout morceau de code doit être facile à comprendre et à utiliser. Avoir une valeur nulle n'est pas le cas.

Amine
la source
Par votre dernière phrase, voulez-vous dire "Vous ne devez retourner null que lorsque le lien réseau est rompu, etc." ou "Vous devez arrêter de renvoyer null lorsque le lien réseau est rompu, etc." ?
Péter Török
@ Péter Török: Je ne recommande pas l'utilisation explicite de "return null;". Cependant, lorsqu'une panne majeure se produit, il est acceptable pour certains services de retourner null.
Amine
Une exception ne serait-elle pas plus appropriée en cas de dysfonctionnement critique inattendu?
Svish
2
@Amine: Je dirais que c'est le cas le moins approprié pour renvoyer null.
Michael Borgwardt
@Svish: si vous pouvez détecter la panne majeure, bien sûr, une exception sera excellente.
Amine
3

En gardant à l'esprit une bonne conception logicielle, vous devez penser à la durée de vie de votre projet.
En rentrant null, comme cela a été dit, vous ne donnez aucune information au client. Regardez ce qui vous est arrivé, au début vous ne saviez pas où était le problème. De plus, si aucune documentation n'est fournie, c'est un gâchis.

En lançant une exception, vous pouvez dire ce qui s'est mal passé. Vous pouvez même personnaliser le texte affiché pour être plus spécifique si vous le souhaitez.

D'un autre côté, en revenant, nullvous incitez le client à enquêter sur ce qui se passe.

De plus, vous avez réalisé qu'il y avait un problème parce que vous êtes allé ailleurs a NullPointerException. Imaginez maintenant que vous ne stockiez pas le retour de cette fonction ... Vous serez obligé d'entourer chaque appel de cette fonction d'un if elsebloc ...

De mon point de vue, le retour nullest une mauvaise pratique.

eversor
la source
2

Le développeur le plus expérimenté qui a écrit le service a fait valoir que demander les données n'est pas une condition d'erreur et que les exceptions ne devraient être levées que dans une condition d'erreur, pas lorsque l'utilisateur n'a pas accès aux données.

De mon point de vue, cela n'a aucun sens.

L'interrogation de données sans autorisation doit entraîner une exception, car il s'agit d'un résultat inattendu - pour obtenir aucun résultat - sans accès approprié.

Analogie : Le responsecode pour un GETsans autorisation n'est pas 200 oksans données, c'est en fait 401 Unauthorized.

Thomas Junk
la source
1

Renvoyer null n'est pas génial. Ça ne vous dit rien. Pour prendre votre exemple, si une application essaie de rechercher quelque chose et que la réponse est nulle pour les deux problèmes de sécurité et si l'élément n'existe pas, comment pouvez-vous faire la différence entre les deux?

Une réponse significative peut permettre à l'application de faire des choix logiques sur ce qu'il faut faire ensuite ou comment résoudre les problèmes.

Qwerky
la source
Vous demandez comment faites-vous la différence entre un objet qui n'est pas là et vous qui n'avez pas la permission de le voir. Peut-être que la conception du système nécessite que vous ne le sachiez pas. Je ne pense pas que nous ayons suffisamment d'informations pour répondre dans ce cas spécifique, et il n'y a pas de réponse qui fonctionne dans tous les cas. Il existe des cas d'utilisation parfaitement valables où, si vous ne pouvez pas le voir, il n'est effectivement pas là. Et, il y a des cas d'utilisation où si vous ne le voyez pas, vous devriez recevoir une exception pour dire pourquoi vous ne pouvez pas le voir.
Bryan Oakley,
Qu'entendez-vous par «la conception du système nécessite que vous ne sachiez pas»? Quel genre de design cela pourrait-il être?
Svish
2
Si la politique de sécurité dit que vous n'êtes pas autorisé à la voir, dans la plupart des cas, vous ne devriez pas non plus être en mesure de savoir qu'elle existe. Cela rend le retour "non trouvé" parfaitement acceptable.
Blrfl
1

Si la cause principale est un utilisateur non authentifié, je préfère une réponse HTTP 401 dure, peut-être avec une redirection vers une jolie page de message (et une notification à quelqu'un qui s'en soucie). Les causes liées à l'autorisation sont plus au cas par cas; il existe des arguments pour renvoyer des erreurs HTTP (403, par exemple) et des arguments pour renvoyer des codes / messages spéciaux bien formés dans la réponse du service.

Les exceptions ne doivent être utilisées que dans des circonstances exceptionnelles et signifient que quelque chose s'est mal passé. Je ne suis pas d'accord pour dire qu'ils devraient être surchargés pour signifier "non autorisés". (La même chose est vraie pour la valeur de retour nulle: je ne suis pas d'accord qu'elle devrait être utilisée pour signifier "non autorisé".)

marque
la source
Eh bien, dans l'interface graphique, vous pouvez le faire, mais en ce qui concerne le bean implémentant le service qui fait les choses en arrière-plan, vous n'avez que des exceptions et des valeurs de retour spéciales à votre disposition. Bien sûr, je ne veux pas dire que l'utilisateur doit obtenir l'exception, mais l'exception pourrait par exemple être interceptée dans le service Web / servlet / quoi que ce soit, puis faire ce qui est approprié. Redirigez vers quelque part, une page http 4xx dure ou autre chose.
Svish
Bon point, mais vous pouvez utiliser un paradigme comme AOP pour capturer ces cas. L'aspect pourrait tester le privilège d'appeler une opération donnée et rediriger / autoriser le traitement à continuer selon les besoins.
Mark
0

Pour jouer l'avocat des démons, j'essaierai de prendre le parti du retour nul.

À mon avis, il n'y a qu'une seule situation où ce type de réponse est presque acceptable et c'est, comme dans ce cas, la sécurité.

Il est très facile de divulguer accidentellement des détails de votre système que les personnes non autorisées ne devraient pas connaître. Cela devrait être évité.

Prenons, par exemple, un vérificateur de nom d'utilisateur et de mot de passe. Le renvoi d'une erreur "Nom d'utilisateur introuvable" et d'une erreur "Mot de passe incorrect" en tant que codes d'erreur distincts vous ouvrirait évidemment à de graves problèmes de sécurité car quelqu'un pourrait d'abord rechercher un nom d'utilisateur valide, puis rechercher un mot de passe. Renvoyer un "nom d'utilisateur / mot de passe invalide" générique serait une meilleure option.

Ainsi, dans ce cas particulier, je dirais qu'il est presque correct de revenir nullmais je suis d'accord, ce serait très désagréable de travailler avec.

OldCurmudgeon
la source
Ouais, je ne serais pas d'accord. À mon avis, la bonne réponse serait une exception, mais la même pour le nom d'utilisateur non trouvé et le mauvais mot de passe. FailedToLogin, InvalidCredentials, peu importe.
Svish
@Svish - Mais si vous essayez de ne donner aucun indice sur la raison de l'échec, le retour nullest exactement la réponse la moins utile. Presque tout le reste pourrait potentiellement être utilisé pour découvrir quelque chose sur le système. OP s'est plainte parce que non seulement le nullretour n'expliquait rien, mais qu'il était même inutile. Tel est le but délibéré ... de ne rien dire et de ne pas aider. Mission accomplie à mon avis.
OldCurmudgeon
Eh bien, c'est peut-être le cas, mais je dirais quand même que c'est mal. Au moins à l'intérieur d'un système. Sur les bords où l'utilisateur final voit les choses, alors bien sûr, je peux être d'accord. Mais à l'intérieur du système, nous, les développeurs, sommes les utilisateurs, et pourquoi voudrions-nous rendre les choses plus confuses pour nous-mêmes? Comme si le développement de logiciels n'était pas déjà assez difficile ...
Svish
1
Et l'exemple de connexion, je dirais qu'il est mauvais pour l'expérience utilisateur de ne pas au moins dire quelque chose au sujet de pourquoi rien ne semblait arrivé quand ils (pensaient qu'ils) sont entrés dans leurs lettres de créance. Si la page est juste actualisée et qu'il semble qu'elle ait simplement ignoré ma tentative de connexion, je suppose que quelque chose ne va pas avec le système, pas que ce que j'ai entré était faux.
Svish
-2

Je pense que return null est hostile, lorsque le client invoque cette méthode et retourne null, le client ne sait pas ce qui s'est passé et pourquoi il a renvoyé null. Nous devrions donc lancer une exception qui est logique et fournir une documentation pour décrire cette exécution.

Mark xie
la source