Patrón de inicio de sesión API REST

181

Estoy creando una API REST, siguiendo de cerca las sugerencias de apigee, usando sustantivos no verbos, la versión de la API al horno en la url, dos rutas de la API por colección, uso de OBTENER POST PUT DELETE, etc.

Estoy trabajando en el sistema de inicio de sesión, pero no estoy seguro de la forma REST adecuada para iniciar sesión en los usuarios. No estoy trabajando en seguridad en este momento, solo en el patrón o flujo de inicio de sesión. (Más adelante agregaremos 2 pasos oAuth, con un HMAC, etc.)

Posibles opciones

  • Un mensaje a algo como https://api...com/v1/login.json
  • Un puesto a algo como https://api...com/v1/users.json
  • Algo que no he pensado de ...

¿Cuál es el estilo REST adecuado para iniciar sesión en los usuarios?

Scott Roepnack
fuente
9
Ese es el formato de respuesta. .json le dice al servidor que responda con json, .xml le dice al servidor que responda con formato xml. Más bien eso lo convierte en un parámetro opcional detrás del?. blog.apigee.com/detail/…
Scott Roepnack
28
Nunca se ha visto una negociación de contenido realizada en la URL, solo en los encabezados. En la URL significa que pierde los beneficios del almacenamiento en caché y más.
Oded
10
@ScottRoepnack, entonces debe considerar el Acceptencabezado HTTP.
Alessandro Vendruscolo
2
@Oded Si usó un Acceptencabezado, también tendría un Vary: Accept, por lo que el almacenamiento en caché no se vería afectado. Conneg en extensión se ha discutido antes ; Sin embargo, estaría de acuerdo con la respuesta de Shonzilla allí.
cmbuckley
2
@Oded: no entiendo. ¿por qué perdería el beneficio del almacenamiento en caché si especifica el tipo de contenido en la URL (ya sea como sufijo .json a la ruta de consulta o como parámetro de consulta type = json)? ¿Y quién es "usted" en este caso? ¿Quién es la persona que pierde los beneficios de almacenamiento en caché? Me parece que los resultados de cualquier consulta pueden almacenarse en caché independientemente de lo que esté en la ruta de consulta o los parámetros.
Cheeso

Respuestas:

138

El diseño basado en principios de la arquitectura web moderna de Roy T. Fielding y Richard N. Taylor , es decir, la secuencia de trabajos de toda la terminología REST, contiene una definición de interacción cliente-servidor:

Todas las interacciones REST son apátridas . Es decir, cada solicitud contiene toda la información necesaria para que un conector comprenda la solicitud, independientemente de cualquier solicitud que pueda haberla precedido .

Esta restricción cumple cuatro funciones, la primera y la tercera son importantes en este caso particular:

  • Primero : elimina cualquier necesidad de que los conectores retengan el estado de la aplicación entre solicitudes , lo que reduce el consumo de recursos físicos y mejora la escalabilidad;
  • 3 ° : permite que un intermediario vea y comprenda una solicitud de forma aislada , lo que puede ser necesario cuando los servicios se reorganizan dinámicamente;

Y ahora volvamos a su caso de seguridad. Cada solicitud debe contener toda la información requerida, y la autorización / autenticación no es una excepción. ¿Cómo lograr esto? Literalmente envíe toda la información requerida por cable con cada solicitud.

Uno de los ejemplos de cómo archivar esto es el código de autenticación de mensaje basado en hash o HMAC . En la práctica, esto significa agregar un código hash del mensaje actual a cada solicitud. Código hash calculado mediante la función hash criptográfica en combinación con una clave criptográfica secreta . La función hash criptográfica está predefinida o es parte de la concepción REST de código a pedido (por ejemplo, JavaScript). La clave criptográfica secreta debe ser proporcionada por el servidor al cliente como recurso, y el cliente la usa para calcular el código hash para cada solicitud.

Hay muchos ejemplos de implementaciones de HMAC , pero me gustaría que preste atención a los siguientes tres:

Cómo funciona en la práctica

Si el cliente conoce la clave secreta, está listo para operar con recursos. De lo contrario, será redirigido temporalmente (código de estado 307 Redirección temporal) para autorizar y obtener una clave secreta, y luego redirigido de nuevo al recurso original. En este caso, no es necesario saber de antemano (es decir, codificar en alguna parte) cuál es la URL para autorizar al cliente , y es posible ajustar este esquema con el tiempo.

Espero que esto te ayude a encontrar la solución adecuada.

Akim
fuente
23
Un MAC está destinada a demostrar mensaje de autenticidad y proteger contra la manipulación de - no tiene nada que ver con la autenticación de usuario
yrk
1
Se agregó uno de los ejemplos, cómo manejar la autenticación de usuario / cliente sin saber de antemano la " URL de inicio de sesión "
Akim
Aquí hay otros dos buenos artículos con ejemplos de autenticación sin estado para servicios REST: blog.jdriven.com/2014/10/… technicalrex.com/2015/02/20/…
Vladimir Rozhkov
41

TL; DR Iniciar sesión para cada solicitud no es un componente requerido para implementar la seguridad API, la autenticación sí lo es.

Es difícil responder a su pregunta sobre el inicio de sesión sin hablar de seguridad en general. Con algunos esquemas de autenticación, no hay inicio de sesión tradicional.

REST no dicta ninguna regla de seguridad, pero la implementación más común en la práctica es OAuth con autenticación de 3 vías (como ha mencionado en su pregunta). No hay inicio de sesión per se, al menos no con cada solicitud de API. Con la autenticación de 3 vías, solo usa tokens.

  1. El usuario aprueba el cliente API y otorga permiso para realizar solicitudes en forma de un token de larga duración
  2. El cliente Api obtiene un token de corta duración utilizando el de larga duración.
  3. El cliente Api envía el token de corta duración con cada solicitud.

Este esquema le da al usuario la opción de revocar el acceso en cualquier momento. Prácticamente todas las API RESTful disponibles públicamente que he visto usan OAuth para implementar esto.

Simplemente no creo que deba enmarcar su problema (y pregunta) en términos de inicio de sesión, sino más bien pensar en asegurar la API en general.

Para obtener más información sobre la autenticación de las API REST en general, puede consultar los siguientes recursos:

Slavo
fuente
¡Sí, oh! Respuesta muy directa, debería ser la respuesta aceptada, en mi humilde opinión.
Levite
26

Una gran parte de la filosofía REST es explotar tantas características estándar del protocolo HTTP como sea posible al diseñar su API. Al aplicar esa filosofía a la autenticación, el cliente y el servidor utilizarían las características de autenticación HTTP estándar en la API.

Las pantallas de inicio de sesión son excelentes para casos de uso de usuarios humanos: visite una pantalla de inicio de sesión, proporcione usuario / contraseña, establezca una cookie, el cliente proporciona esa cookie en todas las solicitudes futuras. No se puede esperar que los humanos que usan navegadores web proporcionen una identificación de usuario y contraseña con cada solicitud HTTP individual.

Pero para una API REST, una pantalla de inicio de sesión y cookies de sesión no son estrictamente necesarias, ya que cada solicitud puede incluir credenciales sin afectar a un usuario humano; y si el cliente no coopera en cualquier momento, 401se puede dar una respuesta "no autorizada". RFC 2617 describe el soporte de autenticación en HTTP.

TLS (HTTPS) también sería una opción y permitiría la autenticación del cliente al servidor (y viceversa) en cada solicitud verificando la clave pública de la otra parte. Además, esto asegura el canal para una bonificación. Por supuesto, un intercambio de pares de claves antes de la comunicación es necesario para hacer esto. (Tenga en cuenta que esto se trata específicamente de identificar / autenticar al usuario con TLS. Asegurar el canal utilizando TLS / Diffie-Hellman siempre es una buena idea, incluso si no identifica al usuario por su clave pública).

Un ejemplo: suponga que un token OAuth son sus credenciales de inicio de sesión completas. Una vez que el cliente tiene el token OAuth, se puede proporcionar como ID de usuario en la autenticación HTTP estándar con cada solicitud. El servidor podría verificar el token en el primer uso y almacenar en caché el resultado de la verificación con un tiempo de vida que se renueva con cada solicitud. Cualquier solicitud que requiera autenticación se devuelve 401si no se proporciona.

fresa
fuente
1
"dado que cada solicitud puede incluir credenciales sin afectar a un usuario humano", se inventaron la autenticación de 3 vías y OAuth porque la cosa en las citas es mala. Si proporciona credenciales con cada solicitud sin un mecanismo en el servidor para revocarlas, eso no sería seguro si se usa sin SSL.
Slavo
1
Siempre que haya un concepto de usuarios, se debe pasar algo del cliente al servidor para identificar qué usuario. Un token OAuth ciertamente puede servir como "credenciales" aquí, en lugar de una combinación de usuario / contraseña real. Asegurar el canal con TLS siempre es algo bueno, pero eso casi no viene al caso. Incluso si usa una cookie, todavía se envía algún tipo de token al servidor con cada solicitud, solo con un encabezado de cookie en lugar de un encabezado de autenticación.
wberry
Y si no está utilizando TLS u OAuth por alguna razón, ¿enviar un usuario / contraseña cada vez es peor que enviarlo solo una vez? Si el atacante puede obtener el usuario / contraseña, es probable que también pueda obtener la cookie de sesión.
wberry
La diferencia entre una cookie y un encabezado de autenticación como credenciales es que las cookies siempre están asociadas a un dominio particular. Esto significa que cuando la API recibe una cookie, sabe de dónde proviene (fue escrita por el mismo dominio anteriormente). Con un encabezado, nunca se sabe y debe implementar comprobaciones específicas para esto. En general, estoy de acuerdo, ambas son credenciales, pero creo que pasar credenciales no es iniciar sesión. Iniciar sesión es la acción activa de abrir la puerta. En el caso de la autenticación de 3 vías, solo la primera aprobación del cliente sería iniciar sesión.
Slavo