Les sessions violent-elles vraiment RESTfulness?

491

L'utilisation de sessions dans une API RESTful viole-t-elle vraiment RESTfulness? J'ai vu beaucoup d'opinions dans les deux sens, mais je ne suis pas convaincu que les sessions soient RESTless . De mon point de vue:

  • l'authentification n'est pas interdite pour RESTfulness (sinon il y aurait peu d'utilité dans les services RESTful)
  • l'authentification se fait en envoyant un jeton d'authentification dans la demande, généralement l'en-tête
  • ce jeton d'authentification doit être obtenu d'une manière ou d'une autre et peut être révoqué, auquel cas il doit être renouvelé
  • le jeton d'authentification doit être validé par le serveur (sinon ce ne serait pas l'authentification)

Alors, comment les sessions violent-elles cela?

  • côté client, les sessions sont réalisées à l'aide de cookies
  • les cookies sont simplement un en-tête HTTP supplémentaire
  • un cookie de session peut être obtenu et révoqué à tout moment
  • les cookies de session peuvent avoir une durée de vie infinie si besoin est
  • l'ID de session (jeton d'authentification) est validé côté serveur

En tant que tel, pour le client, un cookie de session est exactement le même que tout autre mécanisme d'authentification basé sur un en-tête HTTP, sauf qu'il utilise l'en- Cookietête à la place du Authorizationou d'un autre en-tête propriétaire. S'il n'y avait pas de session attachée à la valeur du cookie côté serveur, pourquoi cela ferait-il une différence? L'implémentation côté serveur n'a pas besoin de concerner le client tant que le serveur se comporte RESTful. En tant que tels, les cookies en eux-mêmes ne devraient pas rendre une API RESTless , et les sessions sont simplement des cookies pour le client.

Mes hypothèses sont-elles fausses? Qu'est-ce qui rend les cookies de session RESTless ?

décomposer
la source
5
J'ai couvert ce problème exact ici: stackoverflow.com/questions/1296421/rest-complex-applications/…
Will Hartung
5
Pour ajouter à cela, si vous utilisez uniquement la session pour l'authentification, alors pourquoi ne pas utiliser les en-têtes fournis? Si ce n'est pas le cas et que vous utilisez la session pour un autre état de la conversation, cela viole la contrainte Stateless de REST.
Will Hartung
2
@Will Merci. Il semble que vous parliez de sessions pour stocker temporairement les données soumises par les utilisateurs, alors que dans mon cas, je ne parle que de détails d'implémentation pour l'authentification. Serait-ce là d'où vient le désaccord?
décomposition
3
@deceze Mon seul point est que si vous allez utiliser un en-tête pour représenter un jeton d'authentification, HTTP en fournit un au-delà d'un cookie générique. Alors, pourquoi ne pas l'utiliser et conserver la sémantique gratuite que vous obtenez avec (toute personne voyant la charge utile peut voir qu'un jeton d'authentification lui est attribué).
Will Hartung
7
Bien sûr, mais alors pourquoi ne pas créer vos propres en-têtes, ou détourner un autre en-tête pour le jeton d'authentification. Utilisez l'en-tête X-XYZZY. C'est juste de la syntaxe, non? Les en-têtes véhiculent des informations. L'en-tête d'autorisation est plus "auto-documenté" que votre cookie, car "tout le monde" sait à quoi sert l'en-tête Auth. S'ils voient juste JSESSIONID (ou quoi que ce soit), ils ne peuvent pas faire d'hypothèses, ou pire, faire de fausses hypothèses (à quoi d'autre stocke-t-il dans la session, à quoi d'autre est-il utilisé, etc.). Nommez-vous vos variables dans votre code Aq12hsg? Non bien sûr que non. La même chose s'applique ici.
Will Hartung

Réponses:

299

Définissons d'abord quelques termes:

  • Reposant:

    On peut caractériser les applications conformes aux contraintes REST décrites dans cette section comme "RESTful". [15] Si un service viole l'une des contraintes requises, il ne peut pas être considéré comme RESTful.

    selon wikipedia .

  • contrainte sans état:

    Nous ajoutons ensuite une contrainte à l'interaction client-serveur: la communication doit être de nature sans état, comme dans le style client-serveur sans état (CSS) de la section 3.4.3 (figure 5-3), de sorte que chaque demande du client à Le serveur doit contenir toutes les informations nécessaires à la compréhension de la demande et ne peut tirer parti d'aucun contexte stocké sur le serveur. L'état de la session est donc entièrement conservé par le client.

    selon la thèse de Fielding .

Les sessions côté serveur violent donc la contrainte sans état de REST, et donc RESTfulness.

En tant que tel, pour le client, un cookie de session est exactement le même que tout autre mécanisme d'authentification basé sur un en-tête HTTP, sauf qu'il utilise l'en-tête du cookie au lieu de l'autorisation ou d'un autre en-tête propriétaire.

Par les cookies de session, vous stockez l'état du client sur le serveur et votre demande a donc un contexte. Essayons d'ajouter un équilibreur de charge et une autre instance de service à votre système. Dans ce cas, vous devez partager les sessions entre les instances de service. Il est difficile de maintenir et d'étendre un tel système, il évolue donc mal ...

À mon avis, il n'y a rien de mal avec les cookies. La technologie des cookies est un mécanisme de stockage côté client dans lequel les données stockées sont jointes automatiquement aux en-têtes de cookies par chaque demande. Je ne connais pas de contrainte REST qui a un problème avec ce type de technologie. Il n'y a donc pas de problème avec la technologie elle-même, le problème est avec son utilisation. Fielding a écrit une sous-section expliquant pourquoi il pense que les cookies HTTP sont mauvais.

De mon point de vue:

  • l'authentification n'est pas interdite pour RESTfulness (sinon il y aurait peu d'utilité dans les services RESTful)
  • l'authentification se fait en envoyant un jeton d'authentification dans la demande, généralement l'en-tête
  • ce jeton d'authentification doit être obtenu d'une manière ou d'une autre et peut être révoqué, auquel cas il doit être renouvelé
  • le jeton d'authentification doit être validé par le serveur (sinon ce ne serait pas l'authentification)

Votre point de vue était assez solide. Le seul problème était avec le concept de création de jeton d'authentification sur le serveur. Vous n'avez pas besoin de cette partie. Ce dont vous avez besoin est de stocker le nom d'utilisateur et le mot de passe sur le client et de l'envoyer avec chaque demande. Pour ce faire, vous n'avez pas besoin de plus que l'authentification de base HTTP et une connexion chiffrée:

Figure 1. - Authentification sans état par des clients de confiance

  • Figure 1. - Authentification sans état par des clients de confiance

Vous avez probablement besoin d'un cache d'authentification en mémoire côté serveur pour accélérer les choses, car vous devez authentifier chaque demande.

Maintenant, cela fonctionne assez bien pour les clients de confiance écrits par vous, mais qu'en est-il des clients tiers? Ils ne peuvent pas avoir le nom d'utilisateur et le mot de passe et toutes les autorisations des utilisateurs. Vous devez donc stocker séparément les autorisations qu'un client tiers peut avoir par un utilisateur spécifique. Ainsi, les développeurs de clients peuvent enregistrer leurs clients tiers et obtenir une clé API unique et les utilisateurs peuvent autoriser les clients tiers à accéder à une partie de leurs autorisations. Comme lire le nom et l'adresse e-mail, ou lister leurs amis, etc ... Après avoir autorisé un client tiers, le serveur générera un jeton d'accès. Ces jetons d'accès peuvent être utilisés par le client tiers pour accéder aux autorisations accordées par l'utilisateur, comme suit:

Figure 2. - Authentification sans état par des clients tiers

  • Figure 2. - Authentification sans état par des clients tiers

Ainsi, le client tiers peut obtenir le jeton d'accès d'un client de confiance (ou directement de l'utilisateur). Après cela, il peut envoyer une demande valide avec la clé API et le jeton d'accès. Il s'agit du mécanisme d'authentification tiers le plus basique. Vous pouvez en savoir plus sur les détails d'implémentation dans la documentation de chaque système d'authentification tiers, par exemple OAuth. Bien sûr, cela peut être plus complexe et plus sécurisé, par exemple, vous pouvez signer les détails de chaque demande côté serveur et envoyer la signature avec la demande, etc. La solution réelle dépend des besoins de votre application.

inf3rno
la source
5
Oui, vous avez tout à fait raison. Depuis que j'ai posté cette question, je suis totalement venu pour voir cela. Les cookies de session n'ont rien de spécial lorsqu'ils sont examinés dans les détails techniques, mais il manque la forêt pour les arbres. Accepté votre réponse en raison des beaux graphiques. ;)
décomposer
1
Ok, j'ai repensé, la réponse du service REST ne devrait pas dépendre de l'autorisation, donc je pense que les 2 premières solutions sont 100% correctes, et les autres sont correctes si le service utilise les informations uniquement pour décider s'il autorise la demande ou ne pas. Je pense donc que les autorisations des utilisateurs devraient affecter la représentation de la ressource actuelle.
inf3rno
1
Je vais créer une question sur la dépendance des autorisations des représentations. J'étendrai cette réponse dès que j'aurai la bonne solution.
inf3rno
3
@ inf3rno, il est vrai qu'un service entièrement RESTful ne peut pas dépendre des cookies de session pour l'authentification comme il est traditionnellement implémenté. Cependant, vous pouvez utiliser des cookies pour effectuer l'authentification si le cookie contient toutes les informations d'état dont le serveur aura besoin ultérieurement. Vous pouvez également sécuriser le cookie contre toute falsification en le signant avec une paire de clés publique / privée. Voir mes commentaires ci-dessous.
jcoffland
3
Je ne comprends pas pourquoi tout le monde semble accepter le commentaire, vous devez stocker les mots de passe côté client et les envoyer avec chaque demande. Il s'agit d'une très mauvaise pratique qui met en danger les données sensibles de vos clients. Un mot de passe non haché (qu'il faudrait pour l'envoyer encore et encore) ne doit jamais être stocké nulle part. Si nous l'acceptons, vous utilisez des jetons comme le font la plupart des systèmes d'authentification, auquel cas le mécanisme que nous utilisons pour mettre à l'échelle le référentiel de jetons aura des problèmes d'évolutivité presque identiques à ceux de toute session.
lvoelk
334

Tout d'abord, REST n'est pas une religion et ne doit pas être abordé en tant que tel. Bien qu'il existe des avantages aux services RESTful, vous ne devez suivre les principes de REST que dans la mesure où ils ont un sens pour votre application.

Cela dit, l'authentification et l'état côté client ne violent pas les principes REST. Alors que REST requiert que les transitions d'état soient sans état, cela fait référence au serveur lui-même. Au cœur, tout REST concerne les documents. L'idée derrière l'apatridie est que le SERVEUR est apatride, pas les clients. Tout client émettant une demande identique (mêmes en-têtes, cookies, URI, etc.) doit être amené au même endroit dans l'application. Si le site Web stockait l'emplacement actuel de l'utilisateur et gérait la navigation en mettant à jour cette variable de navigation côté serveur, alors REST serait violé. Un autre client avec des informations de demande identiques serait transféré vers un emplacement différent en fonction de l'état côté serveur.

Les services Web de Google sont un exemple fantastique d'un système RESTful. Ils nécessitent un en-tête d'authentification avec la clé d'authentification de l'utilisateur à transmettre à chaque demande. Cela viole légèrement les principes REST, car le serveur suit l'état de la clé d'authentification. L'état de cette clé doit être conservé et elle a une sorte de date / heure d'expiration après laquelle elle n'accorde plus l'accès. Cependant, comme je l'ai mentionné en haut de mon article, des sacrifices doivent être consentis pour permettre à une application de fonctionner réellement. Cela dit, les jetons d'authentification doivent être stockés de manière à permettre à tous les clients possibles de continuer à accorder l'accès pendant leurs périodes de validité. Si un serveur gère l'état de la clé d'authentification au point qu'un autre serveur à charge équilibrée ne peut pas prendre en charge les demandes basées sur cette clé, vous avez commencé à vraiment violer les principes de REST. Les services de Google garantissent que, à tout moment, vous pouvez prendre un jeton d'authentification que vous utilisiez sur votre téléphone contre le serveur d'équilibrage de charge A et accéder au serveur d'équilibrage de charge B depuis votre bureau et avoir toujours accès au système et être dirigé vers les mêmes ressources si les demandes étaient identiques.

En résumé, vous devez vous assurer que vos jetons d'authentification sont validés par rapport à un magasin de sauvegarde quelconque (base de données, cache, etc.) pour vous assurer de conserver autant de propriétés REST que possible.

J'espère que tout cela a du sens. Vous devriez également consulter la section Contraintes de l'article de wikipedia sur le transfert d'état représentatif si vous ne l'avez pas déjà fait. Elle est particulièrement éclairante en ce qui concerne les arguments avancés par REST et pourquoi.

Jared Harding
la source
6
Je reformulerais votre déclaration initiale. N'utilisez REST que si les contraintes de REST ont un sens pour votre application. Vous êtes libre d'appliquer un sous-ensemble de ces contraintes et vous obtiendrez un sous-ensemble des avantages. Cependant, à ce stade, vous avez créé votre propre style architectural. Ce n'est pas une mauvaise chose, en fait, c'est de cela que parlent les quatre premiers chapitres de la thèse de Roy, la conception fondée sur des principes. REST n'était qu'un exemple.
Darrel Miller
4
@Darrel Un point assez juste. Honnêtement, je ne sais pas comment Google procède, mais le délai d'expiration pourrait être codé dans le jeton d'authentification. Je pense que mon point le plus important est toujours valable. Il existe certains types d'état qui doivent simplement être maintenus et tant que vous comprenez pourquoi REST appelle à l'apatridie, vous pouvez le violer d'une manière qui a du sens sans avoir de nombreuses répercussions sur le reste du système et les avantages d'une architecture RESTful. .
Jared Harding
7
Comme aucun autre argument n'a été avancé jusqu'à présent, j'accepte cette réponse bien écrite. Je pense que la partie importante est que le serveur sans état ne signifie pas le serveur sans état , quelque chose que je pense est souvent mal compris ou mal appliqué. Le serveur peut (et doit généralement ) avoir tout état qu'il souhaite, tant qu'il se comporte de manière idempotente .
décomposition
10
J'ai tellement entendu prêcher que les séances ne sont pas reposantes. L'authentification de base HTTP est un vrai pas en arrière si vous essayez de créer une application Web.
Ben Thurley
1
@Micah Henning, vous faites l'hypothèse fausse que le serveur nécessite des informations d'état pour valider le jeton d'authentification. Nous pouvons raisonnablement supposer que vous ne pouvez pas forger un jeton qui a été signé par une paire de clés publique / privée si vous ne connaissez pas la clé privée. Pour vérifier que le jeton est valide, vous n'avez besoin que de la clé publique. Je maintiens toujours qu'une authentification complètement RESTful est possible.
jcoffland
12

Les cookies ne sont pas destinés à l'authentification. Pourquoi réinventer une roue? HTTP a des mécanismes d'authentification bien conçus. Si nous utilisons des cookies, nous n'utilisons HTTP que comme protocole de transport, donc nous devons créer notre propre système de signalisation, par exemple, pour dire aux utilisateurs qu'ils ont fourni une mauvaise authentification (utiliser HTTP 401 serait incorrect car nous ne le ferions probablement pas fournir Www-Authenticateà un client, comme les spécifications HTTP l'exigent :)). Il convient également de noter que ce Set-Cookien'est qu'une recommandation pour le client. Son contenu peut être ou non enregistré (par exemple, si les cookies sont désactivés), tandis que l'en- Authorizationtête est envoyé automatiquement à chaque demande.

Un autre point est que, pour obtenir un cookie d'autorisation, vous voudrez probablement fournir vos informations d'identification quelque part en premier? Si oui, ne serait-ce pas RESTless? Exemple simple:

  • Vous essayez GET /asans cookie
  • Vous obtenez en quelque sorte une demande d'autorisation
  • Vous allez autoriser en quelque sorte comme POST /auth
  • Vous obtenez Set-Cookie
  • Vous essayez GET /a avec un cookie. Mais GET /ase comporte-t-il idempotamment dans ce cas?

Pour résumer cela, je crois que si nous accédons à une ressource et que nous devons nous authentifier, nous devons nous authentifier sur cette même ressource , pas ailleurs.

starteleport
la source
1
En attendant, je suis également revenu sur ce point de vue. Je pense que techniquement cela fait peu de différence, ce ne sont que des en-têtes HTTP. Il est vrai cependant que le comportement d'authentification lui-même n'est pas RESTful, si une connexion via une adresse distincte est requise. Les cookies ne sont donc que le symptôme d'un problème plus important avec le système d'authentification.
décomposer
Cela ne tient pas vraiment compte du fait que les navigateurs Web prennent uniquement en charge Authorization: Basicou Digest. Si vous voulez faire quelque chose de plus avancé que l'authentification de base ou d'authentification (et vous devriez) dans un contexte de navigateur, alors vous allez avoir besoin d'autre chose que l'en- Authorizationtête.
Oliver Charlesworth
1
Absolument - si vous faites du JS pur, alors tout va bien (sauf, par exemple, les Websockets). Mais mon point est que l'authentification basée sur l'API n'est pas nécessairement la seule considération dans un scénario de navigateur.
Oliver Charlesworth
5
GET /asans cookie et avec cookie sont clairement deux demandes différentes , et il est acceptable qu'ils se comportent différemment.
TRiG
1
Pour ajouter à @TRiG, ​​en suivant cette logique, GET /aavec l'en-tête d'authentification est également le même que GET /asans l'en-tête d'authentification, ce qui le rend également inutilisable pour REST. Si vous allez traiter un en-tête http différemment d'un autre, vous allez au moins y remédier.
Jasper
7

En fait, RESTfulness s'applique uniquement aux RESSOURCES, comme indiqué par un identificateur de ressource universel. Donc, même parler de choses comme les en-têtes, les cookies, etc. en ce qui concerne REST n'est pas vraiment approprié. REST peut fonctionner sur n'importe quel protocole, même s'il se fait régulièrement via HTTP.

Le principal déterminant est le suivant: si vous envoyez un appel REST, qui est un URI, puis une fois que l'appel réussit vers le serveur, cet URI renvoie le même contenu, en supposant qu'aucune transition n'a été effectuée (PUT, POST, DELETE) ? Ce test exclurait les erreurs ou les demandes d'authentification renvoyées, car dans ce cas, la demande n'a pas encore été envoyée au serveur, c'est-à-dire la servlet ou l'application qui renverra le document correspondant à l'URI donné.

De même, dans le cas d'un POST ou PUT, pouvez-vous envoyer un URI / une charge utile donné, et quel que soit le nombre de fois que vous envoyez le message, il mettra toujours à jour les mêmes données, de sorte que les GET ultérieurs renvoient un résultat cohérent?

REST concerne les données d'application, pas les informations de bas niveau requises pour transférer ces données.

Dans le billet de blog suivant, Roy Fielding a donné un bon résumé de toute l'idée REST:

http://groups.yahoo.com/neo/groups/rest-discuss/conversations/topics/5841

"Un système RESTful progresse d'un état stationnaire à l'autre, et chacun de ces états stationnaires est à la fois un état de départ potentiel et un état final potentiel. C'est-à-dire qu'un système RESTful est un nombre inconnu de composants obéissant à un ensemble simple de des règles telles qu'elles sont toujours soit à REST, soit en transition d'un état RESTful à un autre état RESTful. Chaque état peut être complètement compris par la ou les représentations qu'il contient et l'ensemble des transitions qu'il fournit, les transitions étant limitées à un uniforme ensemble d’actions à comprendre. Le système peut être un diagramme d’états complexe, mais chaque agent utilisateur ne peut voir qu’un seul état à la fois (l’état stationnaire actuel) et donc chaque état est simple et peut être analysé indépendamment. l'utilisateur, OTOH, peut créer ses propres transitions à tout moment (par exemple, saisir une URL, sélectionner un signet,ouvrir un éditeur, etc.). "


En ce qui concerne la question de l'authentification, qu'elle soit effectuée via des cookies ou des en-têtes, tant que les informations ne font pas partie de la charge utile URI et POST, cela n'a vraiment rien à voir avec REST. Donc, en ce qui concerne les apatrides, nous parlons uniquement des données d'application.

Par exemple, lorsque l'utilisateur saisit des données dans un écran GUI, le client garde une trace des champs qui ont été saisis, qui ne l'ont pas été, des champs obligatoires manquants, etc. par le serveur. Ce qui est envoyé au serveur, c'est l'ensemble complet des champs qui doivent être modifiés dans la ressource IDENTIFIED (par l'URI), de sorte qu'une transition se produit dans cette ressource d'un état RESTful à un autre.

Ainsi, le client garde une trace de ce que fait l'utilisateur et envoie uniquement des transitions d'état logiquement complètes au serveur.

Ken Kopelson
la source
3
Je ne vois pas comment cela éclaire la question posée.
jcoffland
1

La transaction HTTP, authentification d'accès de base, ne convient pas pour RBAC, car l'authentification d'accès de base utilise le nom d'utilisateur chiffré: mot de passe à chaque fois pour identifier, tandis que ce qui est nécessaire dans RBAC est le rôle que l'utilisateur souhaite utiliser pour un appel spécifique. RBAC ne valide pas les autorisations sur le nom d'utilisateur, mais sur les rôles.

Vous pouvez essayer de concaténer comme ceci: usernameRole: password, mais c'est une mauvaise pratique, et c'est aussi inefficace car lorsqu'un utilisateur a plus de rôles, le moteur d'authentification devra tester tous les rôles en concaténation, et que chaque appel à nouveau. Cela détruirait l'un des plus grands avantages techniques de RBAC, à savoir un test d'autorisation très rapide.

Ce problème ne peut donc pas être résolu à l'aide de l'authentification d'accès de base.

Pour résoudre ce problème, le maintien de session est nécessaire, et cela semble, selon certaines réponses, en contradiction avec REST.

C'est ce que j'aime dans la réponse selon laquelle REST ne doit pas être traité comme une religion. Dans les cas commerciaux complexes, dans les soins de santé, par exemple, le RBAC est absolument courant et nécessaire. Et il serait dommage qu'ils ne soient pas autorisés à utiliser REST car tous les concepteurs d'outils REST traiteraient REST comme une religion.

Pour moi, il n'y a pas beaucoup de façons de maintenir une session sur HTTP. On peut utiliser des cookies, avec un sessionId, ou un en-tête avec un sessionId.

Si quelqu'un a une autre idée, je serai heureuse de l'entendre.

Bert Verhees
la source
-4
  1. Les sessions ne sont pas sans repos
  2. Voulez-vous dire que le service REST pour une utilisation http uniquement ou que je me suis trompé? La session basée sur les cookies doit être utilisée uniquement pour ses propres services (!) Basés sur http! (Il peut être difficile de travailler avec des cookies, par exemple depuis Mobile / Console / Desktop / etc.)
  3. Si vous fournissez un service RESTful pour les développeurs 3D, n'utilisez jamais de session basée sur les cookies, utilisez plutôt des jetons pour éviter les problèmes de sécurité.
Maxime
la source
3
le cookie ne doit pas être utilisé pour stocker une clé de session pour une session sur le serveur qui contient le jeton d'authentification. mais si le cookie contient le jeton d'authentification lui-même, c'est une solution réalisable. (bien sûr, le cookie doit être httponly et sécurisé)
roberkules