security - auth - Mejores prácticas de SPA para la autenticación y la gestión de sesiones
rest api authentication methods (3)
Al crear aplicaciones de estilo SPA utilizando marcos como Angular, Ember, React, etc., ¿qué creen las personas como mejores prácticas para la autenticación y la gestión de sesiones? Puedo pensar en un par de maneras de considerar abordar el problema.
No lo trate de manera diferente a la autenticación con una aplicación web normal, suponiendo que la API y la interfaz de usuario tengan el mismo dominio de origen.
Esto probablemente implicaría tener una cookie de sesión, un almacenamiento de sesión del lado del servidor y probablemente algún punto final de API de sesión que la IU web autenticada pueda obtener para obtener información actual del usuario para ayudar con la personalización o, posiblemente, incluso determinar roles / habilidades en el lado del cliente. El servidor seguiría aplicando las reglas que protegen el acceso a los datos, por supuesto, la interfaz de usuario solo usaría esta información para personalizar la experiencia.
Trátelo como a cualquier cliente de terceros usando una API pública y autentíquese con algún tipo de sistema de token similar a OAuth. Este mecanismo de token sería utilizado por la interfaz de usuario del cliente para autenticar todas y cada una de las solicitudes realizadas a la API del servidor.
Realmente no soy un experto aquí, pero el # 1 parece ser completamente suficiente para la gran mayoría de los casos, pero realmente me gustaría escuchar algunas opiniones más experimentadas.
Esta pregunta se ha abordado, de forma ligeramente diferente, en detalle, aquí:
Pero esto lo aborda desde el lado del servidor. Veamos esto desde el lado del cliente. Antes de hacer eso, sin embargo, hay un preludio importante:
Javascript Crypto es desesperado
El artículo de Matasano sobre esto es famoso, pero las lecciones que contiene son muy importantes:
http://www.matasano.com/articles/javascript-cryptography/
Para resumir:
- Un ataque de hombre en el medio puede reemplazar de manera trivial su código criptográfico con la función
<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
<script> function hash_algorithm(password){ lol_nope_send_it_to_me_instead(password); }</script>
- Un ataque de hombre en el medio es trivial contra una página que sirve cualquier recurso a través de una conexión no SSL.
- Una vez que tienes SSL, estás usando cripto real de todos modos.
Y para añadir un corolario propio:
- Un ataque XSS exitoso puede hacer que un atacante ejecute código en el navegador de su cliente, incluso si está utilizando SSL, por lo que incluso si tiene todas las compuertas bloqueadas, su criptografía del navegador puede fallar si su atacante encuentra la forma de ejecutar Cualquier código javascript en el navegador de otra persona.
Esto hace que muchos de los esquemas de autenticación RESTful sean imposibles o tontos si tiene la intención de utilizar un cliente JavaScript. ¡Miremos!
Autenticación básica HTTP
En primer lugar, HTTP Basic Auth. El esquema más simple: simplemente pase un nombre y una contraseña con cada solicitud.
Esto, por supuesto, requiere absolutamente SSL, porque está pasando un nombre codificado Base64 (reversiblemente) y una contraseña con cada solicitud. Cualquiera que escuche en la línea podría extraer el nombre de usuario y la contraseña de forma trivial. La mayoría de los argumentos de "Autenticación básica es insegura" provienen de un lugar de "Autenticación básica sobre HTTP", lo cual es una idea horrible.
El navegador proporciona soporte para autenticación básica HTTP al horno, pero es feo como pecado y probablemente no deberías usarlo para tu aplicación. La alternativa, sin embargo, es guardar el nombre de usuario y la contraseña en JavaScript.
Esta es la solución más REST. El servidor no requiere ningún conocimiento del estado y autentica cada interacción individual con el usuario. Algunos entusiastas de REST (en su mayoría de hombres de paja) insisten en que mantener cualquier tipo de estado es una herejía y echará espuma en la boca si piensa en cualquier otro método de autenticación. Este tipo de cumplimiento de normas tiene beneficios teóricos (Apache lo soporta de forma inmediata); puede almacenar sus objetos como archivos en carpetas protegidas por archivos .htaccess si lo desea.
El problema Usted está almacenando en caché en el lado del cliente un nombre de usuario y una contraseña. Esto le da a evil.ru una mejor oportunidad: incluso las vulnerabilidades más básicas de XSS podrían hacer que el cliente transfiera su nombre de usuario y contraseña a un servidor malvado. Puede tratar de aliviar este riesgo mediante el hash y el uso de la contraseña, pero recuerde: JavaScript Crypto no tiene remedio . Podría aliviar este riesgo dejándolo en el soporte de Autenticación básica del navegador, pero ... feo como el pecado, como se mencionó anteriormente.
HTTP Digest Auth
¿Es posible la autenticación Digest con jQuery?
Una autenticación más "segura", este es un desafío de hash de solicitud / respuesta. Excepto que JavaScript Crypto no tiene remedio , por lo que solo funciona con SSL y aún tiene que guardar el nombre de usuario y la contraseña en el lado del cliente, lo que lo hace más complicado que la Autenticación básica HTTP, pero no más seguro .
Autenticación de consulta con parámetros de firma adicionales.
Otra autenticación más "segura", donde usted encripta sus parámetros con datos únicos y de tiempo (para proteger contra ataques repetidos y de tiempo) y envía el archivo. Uno de los mejores ejemplos de esto es el protocolo OAuth 1.0, que es, por lo que sé, una forma bastante drástica de implementar la autenticación en un servidor REST.
http://tools.ietf.org/html/rfc5849
Oh, pero no hay clientes OAuth 1.0 para JavaScript. ¿Por qué?
JavaScript Crypto es desesperado , recuerda. JavaScript no puede participar en OAuth 1.0 sin SSL, y aún tiene que almacenar el nombre de usuario y la contraseña del cliente localmente, lo que coloca a este en la misma categoría que Digest Auth, es más complicado que HTTP Basic Auth, pero no es más seguro .
Simbólico
El usuario envía un nombre de usuario y una contraseña y, a cambio, obtiene un token que se puede usar para autenticar las solicitudes.
Esto es ligeramente más seguro que la Autenticación básica HTTP, ya que tan pronto como se complete la transacción de nombre de usuario / contraseña, puede descartar los datos confidenciales. También es menos RESTful, ya que los tokens constituyen "estado" y hacen que la implementación del servidor sea más complicada.
SSL todavía
Sin embargo, el problema es que todavía tiene que enviar el nombre de usuario y la contraseña iniciales para obtener un token. La información sensible todavía toca tu JavaScript comprometible.
Para proteger las credenciales de su usuario, aún necesita mantener a los atacantes fuera de su JavaScript, y también necesita enviar un nombre de usuario y una contraseña a través del cable. SSL requerido
Expiración de fichas
Es común aplicar políticas de token como "hey, cuando este token ha estado presente durante mucho tiempo, descártelo y haga que el usuario se autentique nuevamente". o "Estoy bastante seguro de que la única dirección IP permitida para usar este token es XXX.XXX.XXX.XXX
". Muchas de estas políticas son muy buenas ideas.
Cortafuegos
Sin embargo, el uso de un token sin SSL todavía es vulnerable a un ataque llamado ''sidejacking'': http://codebutler.github.io/firesheep/
El atacante no obtiene las credenciales de su usuario, pero aún pueden pretender ser su usuario, lo que puede ser bastante malo.
tl; dr: enviar tokens sin cifrar a través del cable significa que los atacantes pueden fácilmente atrapar esos tokens y pretender ser su usuario. FireSheep es un programa que hace esto muy fácil.
Una zona separada y más segura
Cuanto mayor sea la aplicación que está ejecutando, más difícil será asegurarse absolutamente de que no podrán inyectar algún código que cambie la forma en que procesa los datos confidenciales. ¿Confías absolutamente en tu CDN? ¿Tus anunciantes? ¿Tu propio código base?
Común para los detalles de la tarjeta de crédito y menos común para el nombre de usuario y la contraseña: algunos implementadores mantienen la "entrada de datos confidenciales" en una página separada del resto de su aplicación, una página que puede controlarse y bloquearse lo mejor posible, preferiblemente una Es difícil engañar a los usuarios con.
Cookie (solo significa Token)
Es posible (y común) poner el token de autenticación en una cookie. Esto no cambia ninguna de las propiedades de auth con el token, es más conveniente. Todos los argumentos anteriores todavía se aplican.
Sesión (todavía solo significa Token)
La autenticación de sesión es solo la autenticación de token, pero con algunas diferencias que hacen que parezca una cosa ligeramente diferente:
- Los usuarios comienzan con un token no autenticado.
- El backend mantiene un objeto ''estado'' que está vinculado al token de un usuario.
- El token se proporciona en una cookie.
- El entorno de aplicación abstrae los detalles de usted.
Aparte de eso, sin embargo, no es diferente de Token Auth, en realidad.
Esto se aleja aún más de una implementación REST: con los objetos de estado, avanzará más y más por el camino del RPC simple en un servidor con estado.
OAuth 2.0
OAuth 2.0 analiza el problema de "Cómo el Software A le da al Software B acceso a los datos del Usuario X sin que el Software B tenga acceso a las credenciales de inicio de sesión del Usuario X".
La implementación es en gran medida una forma estándar para que un usuario obtenga un token, y luego para que un servicio de terceros diga "sip, este usuario y este token coinciden, y puede obtener algunos de sus datos de nosotros ahora".
Fundamentalmente, sin embargo, OAuth 2.0 es solo un protocolo de token. Presenta las mismas propiedades que otros protocolos de token (aún necesita SSL para proteger esos tokens), solo cambia la forma en que se generan esos tokens.
Hay dos maneras en que OAuth 2.0 puede ayudarlo:
- Proporcionar autenticación / información a otros
- Obtención de autenticación / información de otros
Pero cuando se trata de eso, solo estás ... usando tokens.
Volver a tu pregunta
Entonces, la pregunta que está haciendo es "¿debo guardar mi token en una cookie y hacer que la administración automática de sesiones de mi entorno se ocupe de los detalles, o debo guardar mi token en Javascript y manejar esos detalles yo mismo?"
Y la respuesta es: hacer lo que te haga feliz .
Sin embargo, lo que pasa con la administración automática de sesiones es que hay mucha magia detrás de escena para ti. A menudo es mejor controlar esos detalles usted mismo.
Tengo 21 años así que SSL es sí
La otra respuesta es: usar https para todo o los bandidos robarán las contraseñas y tokens de sus usuarios.
Iría por el segundo, el sistema de fichas.
¿Sabías sobre ember-auth o ember-simple-auth ? Ambos usan el sistema basado en token, como los estados de brasa simple:
Una biblioteca liviana y discreta para implementar la autenticación basada en token en las aplicaciones Ember.js. http://ember-simple-auth.simplabs.com
Tienen administración de sesiones y también son fáciles de conectar a proyectos existentes.
También hay una versión de ejemplo de Ember App Kit de ember-simple-auth: Ejemplo de trabajo de ember-app-kit que usa ember-simple-auth para la autenticación OAuth2.
Puede aumentar la seguridad en el proceso de autenticación mediante el uso de JWT (JSON Web Tokens) y SSL / HTTPS.
El ID de sesión / autenticación básica se puede robar a través de:
- Ataque MITM (Man-In-The-Middle) - sin SSL / HTTPS
- Un intruso que accede a la computadora de un usuario.
- XSS
Al utilizar JWT, usted encripta los datos de autenticación del usuario y los almacena en el cliente, y lo envía junto con cada solicitud a la API, donde el servidor / API valida el token. No se puede descifrar / leer sin la clave privada (que el servidor / API almacena en secreto) Leer actualización .
El nuevo flujo (más seguro) sería:
Iniciar sesión
- El usuario inicia sesión y envía las credenciales de inicio de sesión a la API (a través de SSL / HTTPS)
- API recibe credenciales de inicio de sesión
- Si es válido:
- Registrar una nueva sesión en la base de datos Leer actualización
- Cifre la ID de usuario, la ID de sesión, la dirección IP, la marca de tiempo, etc. en un JWT con una clave privada.
- API envía el token JWT de vuelta al cliente (sobre SSL / HTTPS)
- El cliente recibe el token JWT y lo almacena en localStorage / cookie
Cada solicitud a la API
- El usuario envía una solicitud HTTP a la API (sobre SSL / HTTPS) con el token JWT almacenado en el encabezado HTTP
- La API lee el encabezado HTTP y descifra el token JWT con su clave privada
- La API valida el token JWT, compara la dirección IP de la solicitud HTTP con la del token JWT y verifica si la sesión ha caducado
- Si es válido:
- Devuelva la respuesta con el contenido solicitado.
- Si no es válido:
- Lanzar excepción (403/401)
- Intrusión de banderas en el sistema.
- Enviar un correo electrónico de advertencia al usuario.
Actualizado el 30.07.15:
La carga útil / reclamaciones de JWT se pueden leer sin la clave privada (secreta) y no es seguro almacenarla en localStorage. Lo siento por estas declaraciones falsas. Sin embargo, parecen estar trabajando en un estándar JWE (cifrado web JSON) .
Implementé esto almacenando reclamos (userID, exp) en un JWT, lo firmé con una clave privada (secreta) que solo conoce la API / backend y la almacené como una cookie HttpOnly segura en el cliente. De esa manera, no se puede leer a través de XSS y no se puede manipular, de lo contrario, el JWT falla en la verificación de la firma. Además, al utilizar una cookie HttpOnly segura , se asegura de que la cookie se envíe solo a través de solicitudes HTTP (no accesible al script) y solo se envíe a través de una conexión segura (HTTPS).
Actualizado 17.07.16:
JWTs son por naturaleza sin estado. Eso significa que se invalidan / caducan a sí mismos. Al agregar el SessionID en las reclamaciones del token, lo está haciendo con estado, ya que su validez ahora no solo depende de la verificación de la firma y la fecha de caducidad, sino también del estado de la sesión en el servidor. Sin embargo, la ventaja es que puede invalidar tokens / session fácilmente, lo que no se podía hacer antes con JWT sin estado.