javascript - property - title label html
Asegurar OAuth en Javascript (3)
Entonces, tiene un sitio web en example.com y necesita acceso a api.com. Su extensión asume que el usuario ha iniciado sesión en example.com, extrae la cookie de sesión y la pasa a api.com para obtener un token de Oauth. Suena razonable, pero hay formas más fáciles sin tener que escribir complementos del navegador.
En su caso, api.com se comunicará con example.com para verificar la cookie de sesión. Hay una fuerte dependencia entre los dos sistemas. OAuth se usa generalmente cuando example.com y api.com NO se confían entre sí.
Debido a que los dos sistemas ya tienen algún tipo de confianza entre sí, puede hacer varias cosas para simplificar la arquitectura:
- Puede crear un proxy alojado en example.com/api/* que verifique la sesión y luego lo envíe ciegamente a api.com/*. En lo que respecta al navegador, no hay solicitudes de dominio cruzado, por lo que todo funciona muy bien.
- Puede utilizar el inicio de sesión federado a través de dominios. Esto es más complicado que el método proxy, pero puede encontrar fácilmente una implementación existente para su plataforma.
Tengo una api que usa OAuth 1.0a para autenticar las aplicaciones que lo usan. Está reemplazando una antigua API que usó varias llamadas personalizadas y Hodge-Podge que están en desuso.
Es bien sabido que OAuth 1.0a no es seguro en Javascript (del lado del cliente) ya que se basa en que el secreto del consumidor se mantiene en secreto. Lo cual no es posible ya que la fuente siempre es visible.
Tenemos extensiones de navegador para Chrome, Firefox, IE y Safari que necesitan usar esta API en el futuro. Estas extensiones están todas escritas en gran parte o en su totalidad en Javascript, y por lo tanto, el problema de la seguridad.
Estas extensiones son internas y, por lo tanto, pueden tener métodos de autenticación personalizados para obtener sus tokens de acceso.
Lo que estoy planeando implementar es lo siguiente:
- El usuario inicia sesión en el sitio web en el navegador.
- El sitio web les emite una cookie con una clave de sesión.
- Nuestra extensión luego toma esa cookie y la pasa a la api.
- La API valida que es una sesión válida y activa y emite a la extensión sus tokens de acceso.
- Estas fichas duran un máximo de una hora antes de que caduquen.
- También habrá límites más bajos en las cookies emitidas por javascript.
Opera bajo los siguientes supuestos:
- Si otra aplicación tiene acceso a tus cookies, entonces pueden suplantarte en el sitio web, por lo que el acceso a la api no es diferente.
- Todos los métodos de autenticación siguen pasando por nuestro control.
- La caducidad regular de los tokens significa que si están comprometidos, entonces hay un tiempo limitado para la explotación.
Mi pregunta es, ¿es este un método seguro para restringir el acceso a la API? ¿Hay mejores?
Un par de notas. Sé por un hecho que las extensiones de chrome pueden solicitar permiso para acceder a sus cookies para un sitio determinado. Creo que las extensiones de Firefox pueden hacerlo también.
Obviamente, no queremos que nuestras cookies sean accesibles a través de javascript en ninguna página, de lo contrario, estaríamos expuestos a los ataques XSS, por lo que solo deben ser accesibles a través de extensiones.
Escribí un sitio que inicia sesión en OAuth a través de la biblioteca javascript para OAuth. Este es el flujo de trabajo:
- OAuth solo es compatible con los navegadores que tienen LocalStorage
- El formulario de inicio de sesión verificará las claves de LocalAstorage para OAuth e intentará un inicio de sesión de OAuth automáticamente si existen claves OAuth.
- Hay una casilla de verificación para "recordarme" en el formulario de inicio de sesión, por lo que un usuario puede tener tokens OAuth creados para ellos al iniciar sesión.
- Un inicio de sesión exitoso con / recordarme será:
- encuentre o cree una aplicación de cliente con el nombre igual a User Agent, y cree los tokens si es necesario
- Responde con una etiqueta javascript en la respuesta HTML. La etiqueta javascript llamará a una función javascript con los tokens pasados como argumentos. Esta función guardará los tokens de OAuth en LocalStorage.
- Un intento fallido de inicio de sesión de OAuth:
- Responde con una etiqueta javascript en la respuesta HTML. La etiqueta javascript llamará a una función javascript para borrar la configuración de LocalStorage para los tokens de OAuth. Esto evitará intentos adicionales de inicio de sesión OAuth
Hay un poco más de detalles en este proceso, puedo brindarle más información si así lo desea.
Sólo algunos pensamientos para aquellos que vienen a este post después:
"¿Es seguro?" -> depende de qué amenazas queremos proteger. Asumiré en los siguientes puntos que la solución ya implica un enlace de red confiable (para evitar intentos de intercepción de token o credenciales en tránsito). Sin embargo, falta un elemento crucial en la descripción, ya que no menciona si protegemos la API de usuarios no autorizados (seres humanos) o de clientes de API no autorizados (como una extensión maliciosa que se ejecuta dentro del navegador). Lo primero puede lograrse con bastante facilidad con los estándares abiertos disponibles, mientras que uno debe olvidar intentar evitar el acceso desde extensiones no autorizadas, ya que el modelo se basa fundamentalmente en una tecnología del lado del cliente de código abierto. Esto se relaciona con la seguridad de la estación de trabajo más que con el diseño de un mecanismo robusto de autenticación / autorización. Todavía podemos implementar algún tipo de mecanismo de autenticación de extensión, pero será inútil contra alguien que sepa leer el código fuente de las extensiones.
Básicamente hay dos formas en que podemos diseñar la plataforma. Ya sea mediante la instrumentación de la API para permitir la consulta del servicio de autenticación. O mediante el acceso basado en token, en el que la API solicitará la presencia de un token válido en cada solicitud que reciba sin ser su emisor. Esto significaría extender el servicio de autenticación con un nuevo rol: emisor de tickets de API, que rara vez es interesante. Al leer la propuesta, parece que estamos fusionando ambos mundos al enviar el token de sesión a la API. Esto está mal. Primero, esta no es la razón por la que se diseñó el token de sesión basado en cookies. En segundo lugar, obliga a la API a implementar algún tipo de enlace síncrono en tiempo real con el sistema de administración de sesión del servicio de autenticación de usuarios -> acoplamiento que podemos evitar fácilmente.
Asumiré que el objetivo principal es proteger la API de usuarios no autorizados y que no intentaremos abordar las amenazas que se basan en el acceso al sistema local.
Ahora, considerando que no implementaremos la lógica de autenticación en la API, debemos confiar en un modelo donde los usuarios se autentiquen en el servicio de autenticación, permitiendo así que las extensiones subyacentes soliciten un token de acceso.
Esto modifica el escenario original de la siguiente manera:
- El usuario inicia sesión en el sitio web con el navegador.
- El sitio web emite una cookie que contiene una clave de sesión.
- La extensión ahora puede enviar una solicitud de ticket al servicio de autenticación. Las solicitudes incluirán el token de sesión (comportamiento predeterminado del navegador) y, por lo tanto, se autenticarán.
- Una vez que la extensión recibe el ticket, la extensión lo reenvía a la API y solicita un token de sesión.
- La API valida el ticket interrogando al administrador de la sesión. Si el administrador de sesión dice "sí, hice este ticket y aún es válido", la API genera un token de sesión y lo devuelve a la extensión. Este token se insertará en todas las solicitudes posteriores, no en el ticket. Esto evitará cualquier carga de trabajo innecesaria en el administrador de sesión.
- El token (no lo confunda con el ticket) puede tener una vida útil muy corta, por ejemplo, unos minutos -> si caduca, la extensión simplemente regresa al servicio de autenticación y solicita un nuevo ticket (vuelva al paso 3 anterior).
La solución anterior se basa básicamente en lo seguros que están tanto el ticket como el token. Su diseño debe al menos implementar contramedidas contra las siguientes 5 amenazas restantes: i) intentos de adivinar el ticket / token (generación aleatoria suficientemente segura), ii) intentos de calcular el ticket / token (entropía lo suficientemente grande), iii) intentos para reutilizar el ticket / token (vencimiento), iv) intenta manipular un ticket / token válido (verificación de integridad), v) intenta acceder a la API sin un token / ticket válido (validando el token en cada una de las solicitudes que recibe la API ).
Una ventaja adicional de este enfoque es que podemos optimizar la asignación de recursos mediante la emisión de tokens específicos de extensión, que a su vez activarán una lógica específica en la API (acceso a API reducido, vida útil reducida, regulación de solicitudes, etc.)
Espero eso ayude.