¿Las sesiones realmente violan RESTfulness?

491

¿Usar sesiones en una API RESTful realmente viola RESTfulness? He visto muchas opiniones en cualquier dirección, pero no estoy convencido de que las sesiones sean RESTless . Desde mi punto de vista:

  • la autenticación no está prohibida para RESTfulness (de lo contrario, habría poco uso en los servicios RESTful)
  • la autenticación se realiza enviando un token de autenticación en la solicitud, generalmente el encabezado
  • este token de autenticación debe obtenerse de alguna manera y puede revocarse, en cuyo caso debe renovarse
  • el token de autenticación debe ser validado por el servidor (de lo contrario, no sería autenticación)

Entonces, ¿cómo violan las sesiones esto?

  • del lado del cliente, las sesiones se realizan con cookies
  • las cookies son simplemente un encabezado HTTP adicional
  • Se puede obtener y revocar una cookie de sesión en cualquier momento
  • las cookies de sesión pueden tener una vida útil infinita si es necesario
  • el id de sesión (token de autenticación) se valida en el lado del servidor

Como tal, para el cliente, una cookie de sesión es exactamente la misma que cualquier otro mecanismo de autenticación basado en encabezado HTTP, excepto que usa el Cookieencabezado en lugar del Authorizationu otro encabezado propietario. Si no hubo una sesión adjunta al valor del cookie en el lado del servidor, ¿por qué eso marcaría la diferencia? La implementación del lado del servidor no necesita preocupar al cliente siempre que el servidor se comporte RESTful. Como tal, las cookies por sí mismas no deberían hacer que una API RESTless , y las sesiones son simplemente cookies para el cliente.

¿Están equivocados mis supuestos? ¿Qué hace que las cookies de sesión sean RESTless ?

difunto
fuente
55
He cubierto ese problema exacto aquí: stackoverflow.com/questions/1296421/rest-complex-applications/…
Will Hartung
55
Para agregar a eso, si solo está usando la sesión para la autenticación, ¿por qué no usar los encabezados provistos? Si no es así, y está utilizando la sesión para otro estado de la conversación, entonces eso está violando la restricción sin estado de REST.
Will Hartung
2
@ Gracias. Parece que estás hablando de sesiones para almacenar temporalmente datos enviados por el usuario, mientras que en mi caso solo estoy hablando de ellos como un detalle de implementación para la autenticación. ¿Podría ser aquí de donde viene el desacuerdo?
Fallecimiento
3
@deceze Mi único punto es que si va a usar un encabezado para representar un token de autenticación, HTTP proporciona uno más allá de una cookie genérica. Entonces, ¿por qué no usar eso y mantener la semántica gratuita que obtienes? (Cualquiera que vea la carga útil puede ver que hay un token de autenticación asignado).
Will Hartung
77
Claro, pero entonces, ¿por qué no crear sus propios encabezados o secuestrar algún otro encabezado para el token de autenticación? Use el encabezado X-XYZZY. Es solo sintaxis ¿verdad? Los encabezados transmiten información. El encabezado de autorización es más "autodocumentado" que su cookie, porque "todos" saben para qué sirve el encabezado de autenticación. Si solo ven a JSESSIONID (o lo que sea), no pueden hacer suposiciones, o peor aún, hacer las suposiciones equivocadas (qué más está almacenando en la sesión, para qué más se usa esto, etc.). ¿Nombra sus variables en su código Aq12hsg? No claro que no. Lo mismo se aplica aquí.
Will Hartung

Respuestas:

299

Primero, definamos algunos términos:

  • Sosegado:

    Se pueden caracterizar las aplicaciones que se ajustan a las restricciones REST descritas en esta sección como "RESTful". [15] Si un servicio viola alguna de las restricciones requeridas, no puede considerarse RESTful.

    de acuerdo con wikipedia .

  • restricción sin estado:

    A continuación, agregamos una restricción a la interacción cliente-servidor: la comunicación debe ser de naturaleza sin estado, como en el estilo cliente-estado-servidor (CSS) de la Sección 3.4.3 (Figura 5-3), de modo que cada solicitud del cliente a el servidor debe contener toda la información necesaria para comprender la solicitud y no puede aprovechar ningún contexto almacenado en el servidor. Por lo tanto, el estado de la sesión se mantiene completamente en el cliente.

    de acuerdo con la tesis de Fielding .

Entonces, las sesiones del lado del servidor violan la restricción sin estado de REST, y también RESTfulness.

Como tal, para el cliente, una cookie de sesión es exactamente la misma que cualquier otro mecanismo de autenticación basado en encabezado HTTP, excepto que usa el encabezado Cookie en lugar de la Autorización o algún otro encabezado propietario.

Mediante las cookies de sesión, almacena el estado del cliente en el servidor y, por lo tanto, su solicitud tiene un contexto. Intentemos agregar un equilibrador de carga y otra instancia de servicio a su sistema. En este caso, debe compartir las sesiones entre las instancias de servicio. Es difícil mantener y extender dicho sistema, por lo que se escala mal ...

En mi opinión, no hay nada malo con las cookies. La tecnología de cookies es un mecanismo de almacenamiento del lado del cliente en el que los datos almacenados se adjuntan automáticamente a los encabezados de cookies por cada solicitud. No sé de una restricción REST que tenga problemas con ese tipo de tecnología. Por lo tanto, no hay problema con la tecnología en sí, el problema está en su uso. Fielding escribió una subsección sobre por qué piensa que las cookies HTTP son malas.

Desde mi punto de vista:

  • la autenticación no está prohibida para RESTfulness (de lo contrario, habría poco uso en los servicios RESTful)
  • la autenticación se realiza enviando un token de autenticación en la solicitud, generalmente el encabezado
  • este token de autenticación debe obtenerse de alguna manera y puede revocarse, en cuyo caso debe renovarse
  • el token de autenticación debe ser validado por el servidor (de lo contrario, no sería autenticación)

Tu punto de vista era bastante sólido. El único problema fue con el concepto de crear token de autenticación en el servidor. No necesitas esa parte. Lo que necesita es almacenar el nombre de usuario y la contraseña en el cliente y enviarlo con cada solicitud. No necesita más para hacer esto que la autenticación básica HTTP y una conexión cifrada:

Figura 1. - Autenticación sin estado por clientes confiables

  • Figura 1. - Autenticación sin estado por clientes confiables

Probablemente necesite un caché de autenticación en memoria en el lado del servidor para acelerar las cosas, ya que debe autenticar cada solicitud.

Ahora esto funciona bastante bien por clientes de confianza escritos por usted, pero ¿qué pasa con los clientes de terceros? No pueden tener el nombre de usuario y la contraseña y todos los permisos de los usuarios. Por lo tanto, debe almacenar por separado qué permisos puede tener un cliente de terceros por parte de un usuario específico. Por lo tanto, los desarrolladores de clientes pueden registrar sus clientes de terceros y obtener una clave API única y los usuarios pueden permitir que los clientes de terceros accedan a parte de sus permisos. Como leer el nombre y la dirección de correo electrónico, o enumerar a sus amigos, etc. Después de permitir un cliente de terceros, el servidor generará un token de acceso. Estos tokens de acceso pueden ser utilizados por el cliente de terceros para acceder a los permisos otorgados por el usuario, así:

Figura 2. - Autenticación sin estado por clientes de terceros

  • Figura 2. - Autenticación sin estado por clientes de terceros

Por lo tanto, el cliente de terceros puede obtener el token de acceso de un cliente de confianza (o directamente del usuario). Después de eso, puede enviar una solicitud válida con la clave API y el token de acceso. Este es el mecanismo de autenticación de terceros más básico. Puede leer más sobre los detalles de implementación en la documentación de cada sistema de autenticación de terceros, por ejemplo, OAuth. Por supuesto, esto puede ser más complejo y más seguro, por ejemplo, puede firmar los detalles de cada solicitud individual en el lado del servidor y enviar la firma junto con la solicitud, y así sucesivamente ... La solución real depende de la necesidad de su aplicación.

inf3rno
fuente
55
Sí, tienes toda la razón. Desde que publiqué esta pregunta, me he dado cuenta totalmente de verla. Las cookies de sesión no son nada especial cuando se miran en los detalles técnicos, pero le falta el bosque para los árboles. Aceptó su respuesta debido a los buenos gráficos. ;)
deceze
1
Ok, pensé que la respuesta del servicio REST no debería depender de la autorización, por lo que creo que las 2 primeras soluciones son 100% correctas, y las otras están bien si el servicio usa la información solo para decidir si permite la solicitud o no. no. Por lo tanto, creo que los permisos del usuario deberían afectar la representación del recurso actual.
inf3rno
1
Crearé una pregunta sobre la dependencia de permisos de las representaciones. Extenderé esta respuesta tan pronto como tenga la solución adecuada.
inf3rno
3
@ inf3rno, es cierto que un servicio completamente RESTful no puede depender de las cookies de sesión para la autenticación de la forma en que se implementa tradicionalmente. Sin embargo, puede usar cookies para realizar la autenticación si la cookie contiene toda la información de estado que el servidor necesitará más adelante. También puede hacer que la cookie sea segura de la manipulación firmando con un par de claves pública / privada. Ver mis comentarios a continuación.
jcoffland
3
No entiendo por qué todo el mundo parece aceptar el comentario, debe almacenar las contraseñas en el lado del cliente y enviarlas con cada solicitud. Esta es una práctica muy mala y pone en peligro los datos confidenciales de sus clientes. Una contraseña sin contraseña (que tendría que ser para enviarla una y otra vez) nunca debe almacenarse en ningún lugar. Si aceptamos esto, entonces está utilizando tokens como la mayoría de los sistemas de autenticación, en cuyo caso cualquier mecanismo que usemos para escalar el repositorio de tokens tendrá preocupaciones de escalabilidad en su mayoría iguales a las de cualquier escalabilidad de sesión.
lvoelk
334

En primer lugar, REST no es una religión y no debe abordarse como tal. Si bien los servicios RESTful tienen ventajas, solo debe seguir los principios de REST en la medida en que tengan sentido para su aplicación.

Dicho esto, la autenticación y el estado del lado del cliente no violan los principios REST. Si bien REST requiere que las transiciones de estado no tengan estado, esto se refiere al servidor en sí. En el fondo, todo REST se trata de documentos. La idea detrás de la apatridia es que el SERVIDOR no tiene estado, no los clientes. Cualquier cliente que emita una solicitud idéntica (mismos encabezados, cookies, URI, etc.) debe ser llevado al mismo lugar en la aplicación. Si el sitio web almacenó la ubicación actual del usuario y administró la navegación actualizando esta variable de navegación del lado del servidor, se violaría REST. Otro cliente con información de solicitud idéntica sería llevado a una ubicación diferente dependiendo del estado del lado del servidor.

Los servicios web de Google son un ejemplo fantástico de un sistema RESTful. Requieren que se envíe un encabezado de autenticación con la clave de autenticación del usuario a cada solicitud. Esto viola ligeramente los principios REST, porque el servidor está rastreando el estado de la clave de autenticación. El estado de esta clave debe mantenerse y tiene algún tipo de fecha / hora de vencimiento después de la cual ya no otorga acceso. Sin embargo, como mencioné en la parte superior de mi publicación, se deben hacer sacrificios para permitir que una aplicación realmente funcione. Dicho esto, los tokens de autenticación deben almacenarse de una manera que permita a todos los clientes posibles continuar otorgando acceso durante sus tiempos válidos. Si un servidor administra el estado de la clave de autenticación hasta el punto de que otro servidor con equilibrio de carga no puede hacerse cargo de cumplir con las solicitudes basadas en esa clave, has comenzado a violar realmente los principios de REST. Los servicios de Google aseguran que, en cualquier momento, puede tomar un token de autenticación que estaba usando en su teléfono contra el servidor de equilibrio de carga A y golpear el servidor de equilibrio de carga B desde su escritorio y aún tener acceso al sistema y ser dirigido a los mismos recursos si Las solicitudes fueron idénticas.

Todo se reduce a que debe asegurarse de que sus tokens de autenticación estén validados contra un almacén de respaldo de algún tipo (base de datos, caché, lo que sea) para garantizar que conserve la mayor cantidad posible de propiedades REST.

Espero que todo eso tenga sentido. También debe consultar la sección Restricciones del artículo de wikipedia sobre Transferencia de estado representacional si aún no lo ha hecho. Es particularmente esclarecedor con respecto a lo que los principios de REST realmente defienden y por qué.

Jared Harding
fuente
66
Reformularía tu declaración inicial. Solo use REST si las restricciones de REST tienen sentido para su aplicación. Usted es libre de aplicar un subconjunto de esas restricciones y obtendrá un subconjunto de los beneficios. Sin embargo, en ese momento, ha creado su propio estilo arquitectónico. Sin embargo, eso no es malo, de hecho, de eso se tratan los primeros cuatro capítulos de la disertación de Roy, el diseño basado en principios. REST fue solo un ejemplo.
Darrel Miller
44
@Darrel Un punto bastante justo. Sinceramente, no estoy seguro de cómo lo hace Google, pero el tiempo de caducidad podría codificarse en el token de autenticación. Sin embargo, creo que mi punto más grande sigue en pie. Hay algunos tipos de estado que simplemente deben mantenerse y siempre y cuando comprenda por qué REST llama a la apatridia, puede violarlo de una manera que tenga sentido sin muchas repercusiones en el resto del sistema y las ventajas de una arquitectura RESTful .
Jared Harding
77
Como hasta ahora no se han presentado otros argumentos, acepto esta respuesta bien escrita. Creo que la parte importante es que servidor sin estado no significa servidor sin estado , algo que creo que a menudo es mal entendido o mal aplicado. El servidor puede (y generalmente debe ) tener el estado que desee, siempre que se comporte idempotente .
Fallecimiento
10
He escuchado tanta predicación que las sesiones no son relajantes. Sin embargo, la autenticación básica HTTP es un verdadero paso atrás si está intentando crear una aplicación web.
Ben Thurley
1
@Micah Henning, está asumiendo falsamente que el servidor requiere información de estado para validar el token de autenticación. Podemos suponer razonablemente que no puede falsificar un token firmado por un par de claves pública / privada si no conoce la clave privada. Para verificar que el token es válido, todo lo que necesita es la clave pública. Todavía sostengo que es posible una autenticación completamente RESTful.
jcoffland
12

Las cookies no son para autenticación. ¿Por qué reinventar una rueda? HTTP tiene mecanismos de autenticación bien diseñados. Si usamos cookies, caemos en el uso de HTTP solo como protocolo de transporte, por lo tanto, necesitamos crear nuestro propio sistema de señalización, por ejemplo, para decirles a los usuarios que proporcionaron una autenticación incorrecta (usar HTTP 401 sería incorrecto, ya que probablemente no lo haríamos). suministro Www-Authenticatea un cliente, ya que las especificaciones HTTP requieren :)). También debe tenerse en cuenta que Set-Cookiees solo una recomendación para el cliente. Su contenido puede guardarse o no (por ejemplo, si las cookies están deshabilitadas), mientrasAuthorization encabezado se envía automáticamente en cada solicitud.

Otro punto es que, para obtener una cookie de autorización, ¿es probable que primero desee proporcionar sus credenciales? Si es así, ¿no sería RESTless? Ejemplo simple:

  • Lo intentas GET /a sin cookie
  • Recibes una solicitud de autorización de alguna manera
  • Vas y autorizas de alguna manera POST /auth
  • Usted obtiene Set-Cookie
  • Lo intentas GET /a con galleta. ¿Pero se GET /acomporta de manera idempotente en este caso?

Para resumir esto, creo que si accedemos a algún recurso y necesitamos autenticarnos, entonces debemos autenticarnos en ese mismo recurso , no en ningún otro lugar.

Starteleport
fuente
1
Mientras tanto, también me acerqué más a este punto de vista. Creo que técnicamente hace poca diferencia, solo son encabezados HTTP. Sin embargo, es cierto que el comportamiento de autenticación en sí no es RESTful, si se requiere iniciar sesión a través de una dirección separada. Por lo tanto, las cookies son solo un síntoma de un problema mayor con el sistema de autenticación.
deceze
Esto realmente no explica el hecho de que los navegadores web solo admiten Authorization: Basico Digest. Si desea hacer algo más avanzado que la autenticación básica o de resumen (y debería hacerlo) en un contexto de navegador, necesitará algo más que el Authorizationencabezado.
Oliver Charlesworth
1
Absolutamente: si estás haciendo JS puro, entonces las cosas están básicamente bien (excepto, por ejemplo, Websockets). Pero mi punto es que la autenticación basada en API no es necesariamente la única consideración en un escenario de navegador.
Oliver Charlesworth
55
GET /asin una cookie y con una cookie son claramente dos solicitudes diferentes , y es aceptable que se comporten de manera diferente.
TRiG
1
Para agregar a @TRiG, ​​seguir esta lógica, GET /acon el encabezado de autenticación también es lo mismo que GET /asin el encabezado de autenticación, lo que lo hace igualmente inutilizable para REST. Si va a tratar un encabezado http de manera diferente a otro, al menos lo abordará.
Jasper
7

En realidad, RESTfulness solo se aplica a los RECURSOS, como lo indica un Identificador Universal de Recursos. Por lo tanto, incluso hablar de cosas como encabezados, cookies, etc. con respecto a REST no es realmente apropiado. REST puede funcionar sobre cualquier protocolo, a pesar de que se realiza rutinariamente a través de HTTP.

El principal determinante es este: si envía una llamada REST, que es un URI, una vez que la llamada se realiza con éxito al servidor, ese URI devuelve el mismo contenido, suponiendo que no se hayan realizado transiciones (PUT, POST, DELETE) ? Esta prueba excluiría errores o solicitudes de autenticación que se devuelven, porque en ese caso, la solicitud aún no se ha enviado al servidor, es decir, el servlet o la aplicación que devolverá el documento correspondiente al URI dado.

Del mismo modo, en el caso de un POST o PUT, ¿puede enviar un URI / carga útil dado, e independientemente de cuántas veces envíe el mensaje, siempre actualizará los mismos datos, de modo que los GET posteriores arrojarán un resultado consistente?

REST trata sobre los datos de la aplicación, no sobre la información de bajo nivel requerida para transferir esos datos.

En la siguiente publicación de blog, Roy Fielding dio un buen resumen de toda la idea REST:

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

"Un sistema RESTful progresa de un estado estable al siguiente, y cada uno de estos estados estables es tanto un estado inicial potencial como un estado final potencial. Es decir, un sistema RESTful es un número desconocido de componentes que obedecen a un conjunto simple de reglas tales que siempre están en REST o en transición de un estado RESTful a otro estado RESTful. Cada estado puede ser completamente entendido por la (s) representación (es) que contiene y el conjunto de transiciones que proporciona, con las transiciones limitadas a un uniforme conjunto de acciones para que sea comprensible. El sistema puede ser un diagrama de estado complejo, pero cada agente de usuario solo puede ver un estado a la vez (el estado estable actual) y, por lo tanto, cada estado es simple y se puede analizar de forma independiente. el usuario, OTOH, puede crear sus propias transiciones en cualquier momento (por ejemplo, ingrese una URL, seleccione un marcador,abrir un editor, etc.) ".


En cuanto al tema de la autenticación, ya sea que se realice a través de cookies o encabezados, siempre que la información no sea parte de la carga útil de URI y POST, realmente no tiene nada que ver con REST. Entonces, en lo que respecta a ser apátrida, estamos hablando solo de los datos de la aplicación.

Por ejemplo, a medida que el usuario ingresa datos en una pantalla GUI, el cliente realiza un seguimiento de los campos que se ingresaron, los que no, los campos obligatorios que faltan, etc. por el servidor Lo que se envía al servidor es el conjunto completo de campos que deben modificarse en el recurso IDENTIFICADO (por el URI), de modo que se produzca una transición en ese recurso de un estado RESTful a otro.

Por lo tanto, el cliente realiza un seguimiento de lo que está haciendo el usuario y solo envía transiciones de estado lógicamente completas al servidor.

Ken Kopelson
fuente
3
No veo cómo esto arroja alguna luz sobre la pregunta planteada.
jcoffland
1

La transacción HTTP, autenticación de acceso básico, no es adecuada para RBAC, porque la autenticación de acceso básico utiliza el nombre de usuario cifrado: contraseña cada vez que se identifica, mientras que lo que se necesita en RBAC es el Rol que el usuario desea usar para una llamada específica. RBAC no valida permisos en nombre de usuario, sino en roles.

Podría dar vueltas para concatenar de esta manera: usernameRole: password, pero esta es una mala práctica, y también es ineficiente porque cuando un usuario tiene más roles, el motor de autenticación necesitaría probar todos los roles en la concatenación, y cada llamada nuevamente. Esto destruiría una de las mayores ventajas técnicas de RBAC, es decir, una prueba de autorización muy rápida.

Por lo tanto, ese problema no se puede resolver con la autenticación de acceso básica.

Para resolver este problema, es necesario mantener la sesión y, según algunas respuestas, parece estar en contradicción con REST.

Eso es lo que me gusta de la respuesta de que REST no debe tratarse como una religión. En casos de negocios complejos, en el cuidado de la salud, por ejemplo, RBAC es absolutamente común y necesario. Y sería una pena que no se les permitiera usar REST porque todos los diseñadores de herramientas REST tratarían REST como una religión.

Para mí no hay muchas formas de mantener una sesión a través de HTTP. Se pueden usar cookies, con un Id. De sesión, o un encabezado con un Id. De sesión.

Si alguien tiene otra idea, me alegrará escucharla.

Bert Verhees
fuente
-4
  1. Las sesiones no son RESTless
  2. ¿Quieres decir que el servicio REST solo para uso http o me equivoqué? ¡La sesión basada en cookies debe usarse solo para servicios propios (!) Basados ​​en http! (Podría ser un problema trabajar con cookies, por ejemplo, desde Mobile / Console / Desktop / etc.)
  3. si proporciona un servicio RESTful para desarrolladores de fiestas en 3D, nunca use una sesión basada en cookies, use tokens en su lugar para evitar los problemas de seguridad.
Máxima
fuente
3
la cookie no debe usarse para almacenar una clave de sesión para una sesión en el servidor que contiene el token de autenticación. pero si la cookie contiene el token de autenticación, es una solución factible. (por supuesto, la cookie debe estar httponly y segura)
roberkules