Creación de una API para aplicaciones móviles: autenticación y autorización

189

Visión general

Estoy buscando crear una API (REST) ​​para mi aplicación. El propósito inicial / primario será el consumo de aplicaciones móviles (iPhone, Android, Symbian, etc.). He estado buscando diferentes mecanismos de autenticación y autorización para API basadas en web (estudiando otras implementaciones). Tengo la cabeza concentrada en la mayoría de los conceptos fundamentales, pero todavía estoy buscando orientación en algunas áreas. Lo último que quiero hacer es reinventar la rueda, pero no encuentro ninguna solución estándar que se ajuste a mis criterios (sin embargo, mis criterios pueden estar equivocados, así que siéntete libre de criticar eso también). Además, quiero que la API sea la misma para todas las plataformas / aplicaciones que la consumen.

oAuth

Seguiré adelante y rechazaré mi objeción a oAuth, ya que sé que probablemente será la primera solución ofrecida. Para aplicaciones móviles (o más específicamente aplicaciones no web), parece incorrecto abandonar la aplicación (para ir a un navegador web) para la autenticación. Además, no hay forma (que yo sepa) de que el navegador devuelva la devolución de llamada a la aplicación (especialmente multiplataforma). Conozco un par de aplicaciones que hacen eso, pero se siente mal y da un descanso en la aplicación UX.

Requisitos

  1. El usuario ingresa el nombre de usuario / contraseña en la aplicación.
  2. Cada llamada a la API se identifica mediante la aplicación de llamada.
  3. Los gastos generales se mantienen al mínimo y el aspecto de autenticación es intuitivo para los desarrolladores.
  4. El mecanismo es seguro tanto para el usuario final (sus credenciales de inicio de sesión no están expuestas) como para el desarrollador (sus credenciales de aplicación no están expuestas).
  5. Si es posible, no requiera https (de ninguna manera es un requisito difícil).

Mis pensamientos actuales sobre la implementación

Un desarrollador externo solicitará una cuenta API. Recibirán un apikey y un apisecret. Cada solicitud requerirá como mínimo tres parámetros.

  • apikey - dada al desarrollador en el registro
  • marca de tiempo: funciona como un identificador único para cada mensaje para una apikey dada
  • hash: un hash de la marca de tiempo + el apisecreto

Se requiere que la apikey identifique la aplicación que emite la solicitud. La marca de tiempo actúa de manera similar a oauth_nonce y evita / mitiga los ataques de repetición. El hash asegura que la solicitud fue emitida realmente por el propietario de la apikey dada.

Para las solicitudes autenticadas (las que se realizan en nombre de un usuario), todavía estoy indeciso entre ir con una ruta access_token o un combo de hash de nombre de usuario y contraseña. De cualquier manera, en algún momento se requerirá un combo de nombre de usuario / contraseña. Entonces, cuando lo haga, se usará un hash de varias piezas de información (apikey, apisecret, timestamp) + la contraseña. Me encantaría recibir comentarios sobre este aspecto. Para su información, primero tendrían que cambiar la contraseña, ya que no almaceno las contraseñas en mi sistema sin hash.

Conclusión

Para su información, esta no es una solicitud de cómo construir / estructurar la API en general, solo cómo manejar la autenticación y la autorización únicamente desde una aplicación.

Pensamientos aleatorios / preguntas extra

Para las API que solo requieren una apikey como parte de la solicitud, ¿cómo puede evitar que alguien que no sea el propietario de la apikey pueda ver la apikey (ya que se envió en claro) y realizar solicitudes excesivas para superar los límites de uso? Quizás acabo de pensar en esto, pero ¿no debería haber algo para autenticar que se verificó una solicitud al propietario de la apikey? En mi caso, ese era el propósito del apisecreto, nunca se muestra / transmite sin ser hash.

Hablando de hashes, ¿qué pasa con md5 vs hmac-sha1? ¿Realmente importa cuando todos los valores se combinan con datos suficientemente largos (es decir, apisecret)?

Anteriormente había estado considerando agregar una sal por usuario / fila al hash de contraseña de mis usuarios. Si tuviera que hacer eso, ¿cómo podría la aplicación ser capaz de crear un hash coincidente sin conocer la sal utilizada?

jsuggs
fuente
1
Esperaba recibir algunos comentarios / sugerencias más. ¿Es la pregunta demasiado vaga / ambigua?
jsuggs
66
la pregunta es perfecta pero, incluso casi 2 años después, las implementaciones de Oauth parecen ser arcanas ... Me está costando mucho lograr exactamente lo que ha discutido anteriormente. Tengo una dimensión adicional: no quiero usar un par de nombre de usuario / contraseña: quiero usar la verificación de identidad de Google en Android / iOS (WWF declaró a Symbian como una "especie casi extinta") y me niego a desarrollar Windows Mobile (como lo llamen en estos días).
tony gil
8
es ridículo que, por mucho que todos sugieran oauth 2.0, aún no haya encontrado un tutorial o ejemplo claro y simple que use inglés común para explicar los pasos, requisitos, qué hacer y qué no hacer ...
ChuckKelly
2
Lea este flujo específico de OAuth2.0 (El flujo de contraseña del propietario del recurso). No redirigir al sitio web. techblog.hybris.com/2012/06/11/…
Franklin
1
También estoy buscando la misma respuesta. Encontré un buen artículo que fue escrito recientemente. Espero que esto ayude. stormpath.com/blog/the-ultimate-guide-to-mobile-api-security
Nuevo en Rails

Respuestas:

44

La forma en que pienso hacer la parte de inicio de sesión de esto en mis proyectos es:

  1. antes de iniciar sesión, el usuario solicita una login_tokendel servidor. Estos se generan y almacenan en el servidor a pedido, y probablemente tengan una vida útil limitada.

  2. para iniciar sesión, la aplicación calcula el hash de la contraseña de los usuarios, luego usa la contraseña login_tokenpara obtener un valor, luego devuelve login_tokenel hash combinado y el hash.

  3. El servidor comprueba si login_tokenes el que ha generado, eliminándolo de su lista de correos válidos login_token. Luego, el servidor combina el hash almacenado de la contraseña del usuario con el login_tokeny se asegura de que coincida con el token combinado enviado. Si coincide, ha autenticado a su usuario.

Las ventajas de esto son que nunca almacena la contraseña del usuario en el servidor, la contraseña nunca se pasa en claro, el hash de contraseña solo se pasa en claro en la creación de la cuenta (aunque puede haber formas de evitar esto), y debería ser a salvo de ataques de repetición ya que login_tokense elimina de la base de datos en uso.

Michael Anderson
fuente
Gracias, olvidé agregar la parte sobre el hash de la contraseña en el lado de la aplicación. No almaceno las contraseñas de mis usuarios de forma clara (hago un hash antes de almacenarlas).
jsuggs
2
Veo una desventaja de este método: no puede almacenar contraseñas con sal en DB. Si los atacantes ponen sus manos sobre tu base de datos, no necesitarán descifrar. Dado que el hash de contraseña es la contraseña real en este esquema.
sigod
2
@sigod Tienes razón. Aunque creo que hay una dicotomía fundamental allí: debe confiar en su transporte o confiar en su almacenamiento. Los sistemas de inicio de sesión que usan contraseñas con sal confían en la capa de transporte, y así pasan la contraseña del usuario al sistema de autenticación de forma clara. Este caso no es confiar en la capa de transporte (creo que esto se debió a que la plataforma a la que me dirigía tenía un mal soporte para SHTTP). Si confía en la capa de transporte, puede hacer otras compensaciones.
Michael Anderson
Tengo 3 problemas: 1) ¿cómo nunca se almacena la contraseña del usuario en el servidor? en el paso 2 mencionaste que almacena la contraseña del usuario hash. 2) La contraseña del usuario nunca se pasa en claro. Pero en realidad está en hash en el cliente y luego se compara con la contraseña en hash en el servidor, que es casi lo mismo que pasarla y almacenarla en claro, ¿cuál es el punto de hacerlo? 3) Este método supone que el atacante no sabe cómo hash login_token + contraseña, lo que viola el principio de Kerckhoff y lo hace realmente inseguro.
Tamer Shlash
14

Esa es una gran cantidad de preguntas en una, creo que algunas personas no lograron leer hasta el final :)

Mi experiencia con la autenticación del servicio web es que las personas generalmente la diseñan en exceso, y los problemas son los mismos que encontraría en una página web. Las posibles opciones muy simples incluirían https para el paso de inicio de sesión, devolver un token, requerir que se incluya con futuras solicitudes. También puede usar la autenticación básica http y simplemente pasar cosas en el encabezado. Para mayor seguridad, gire / caduque los tokens con frecuencia, verifique que las solicitudes provengan del mismo bloque de IP (esto podría ser complicado, ya que los usuarios móviles se mueven entre celdas), combínelas con la clave API o similar. Alternativamente, realice el paso de "clave de solicitud" de oauth (alguien ya sugirió esto en una respuesta anterior y es una buena idea) antes de autenticar al usuario, y utilícelo como una clave requerida para generar el token de acceso.

Una alternativa que aún no he usado, pero que he escuchado mucho sobre como una alternativa amigable para dispositivos de oAuth es XAUTH . Eche un vistazo y si lo usa, me interesaría saber cuáles son sus impresiones.

Para el hashing, sha1 es un poco mejor, pero no se obsesione con eso; lo que sea que los dispositivos puedan implementar fácilmente (y rápidamente en un sentido de rendimiento) probablemente esté bien.

Espero que ayude, buena suerte :)

Lorna Mitchell
fuente
Gracias por la respuesta. He estado buscando en xAuth y esa podría ser la ruta que sigo para poder terminar con una instalación de oAuth, lo que lo convierte en un proceso más estandarizado para interactuar con la API.
jsuggs
9

Entonces, ¿lo que buscas es algún tipo de mecanismo de autenticación del lado del servidor que maneje los aspectos de autenticación y autorización de una aplicación móvil?

Suponiendo que este sea el caso, entonces lo abordaría de la siguiente manera (pero solo porque soy un desarrollador de Java, por lo que un chico de C # lo haría de manera diferente):

El servicio RESTful de autenticación y autorización

  1. Esto funcionará solo sobre HTTPS para evitar escuchas.
  2. Se basará en una combinación de RESTEasy , Spring Security y CAS (para inicio de sesión único en múltiples aplicaciones).
  3. Funcionará tanto con navegadores como con aplicaciones cliente habilitadas para la web.
  4. Habrá una interfaz de administración de cuentas basada en la web para permitir a los usuarios editar sus detalles, y los administradores (para aplicaciones particulares) para cambiar los niveles de autorización

La biblioteca / aplicación de seguridad del lado del cliente

  1. Para cada plataforma compatible (por ejemplo, Symbian, Android, iOS, etc.) cree una implementación adecuada de la biblioteca de seguridad en el idioma nativo de la plataforma (por ejemplo, Java, ObjectiveC, C, etc.)
  2. La biblioteca debe gestionar la formación de solicitudes HTTPS utilizando las API disponibles para la plataforma dada (por ejemplo, Java usa URLConnection, etc.)
  3. Los consumidores de la biblioteca general de autenticación y autorización (porque eso es todo) codificarán una interfaz específica y no estarán contentos si alguna vez cambia, así que asegúrese de que sea muy flexible. Siga las opciones de diseño existentes, como Spring Security.

Entonces, ahora que la vista desde 30,000 pies está completa, ¿cómo lo haces? Bueno, no es tan difícil crear un sistema de autenticación y autorización basado en las tecnologías enumeradas en el lado del servidor con un cliente de navegador. En combinación con HTTPS, los marcos proporcionarán un proceso seguro basado en un token compartido (generalmente presentado como una cookie) generado por el proceso de autenticación y utilizado siempre que el usuario desee hacer algo. El cliente presenta este token al servidor cada vez que se realiza una solicitud.

En el caso de la aplicación móvil local, parece que está buscando una solución que haga lo siguiente:

  1. La aplicación cliente tiene una Lista de control de acceso (ACL) definida que controla el acceso en tiempo de ejecución a las llamadas a métodos. Por ejemplo, un usuario determinado puede leer una colección de un método, pero su ACL solo permite el acceso a objetos que tienen una Q en su nombre, por lo que el interceptor de seguridad extrae silenciosamente algunos datos de la colección. En Java esto es sencillo, solo usa las anotaciones de Spring Security en el código de llamada e implementa un proceso de respuesta ACL adecuado. En otros idiomas, usted está solo y probablemente necesite proporcionar un código de seguridad repetitivo que llame a su biblioteca de seguridad. Si el lenguaje es compatible con AOP (Programación Orientada a Aspectos), úselo al máximo para esta situación.
  2. La biblioteca de seguridad almacena en caché la lista completa de autorizaciones en su memoria privada para la aplicación actual para que no tenga que permanecer conectada. Dependiendo de la duración de la sesión de inicio de sesión, esta podría ser una operación única que nunca se repite.

Hagas lo que hagas, no intentes inventar tu propio protocolo de seguridad ni uses la seguridad por la oscuridad. Nunca podrá escribir un algoritmo mejor para esto que los que están actualmente disponibles y son gratuitos. Además, las personas confían en algoritmos bien conocidos. Entonces, si dice que su biblioteca de seguridad proporciona autorización y autenticación para aplicaciones móviles locales que utilizan una combinación de tokens cifrados SSL, HTTPS, SpringSecurity y AES, inmediatamente tendrá credibilidad en el mercado.

Espero que esto ayude, y buena suerte con tu empresa. Si desea más información, hágamelo saber: he escrito bastantes aplicaciones web basadas en Spring Security, ACL y similares.

Gary Rowe
fuente
Gracias, buena información. Par de preguntas. Primero, si las escuchas son aceptables (no estoy seguro de si es / no, mi solicitud en mente no tiene ninguna información personal / valiosa real, pero si lo hiciera, mi justificación cambiaría), ¿entonces se requiere HTTPS?
jsuggs
Puede operar el sistema general fuera de HTTPS si lo desea. HTTPS es solo para proteger la información secreta, por lo que supongo que durante la fase de autenticación lo haría a través de HTTPS para garantizar que su nombre de usuario / contraseña / secreto se mantenga en secreto. Después de que se entregue el token en la respuesta, se pueden hacer más solicitudes de forma clara si la información contenida en la secuencia (que se requiere autenticación para obtener) no necesita protección contra los espías.
Gary Rowe
Además, esta descripción del protocolo de autenticación CAS puede ser útil: jasig.org/cas/protocol
Gary Rowe
9

Twitter abordó el problema de la aplicación externa en oAuth al admitir una variante que llaman xAuth . Desafortunadamente, ya hay una gran cantidad de otros esquemas con este nombre, por lo que puede ser confuso resolverlo.

El protocolo es oAuth, excepto que omite la fase de token de solicitud y simplemente emite inmediatamente un par de tokens de acceso al recibir un nombre de usuario y contraseña. (A partir del paso E aquí .) Esta inicial solicitud y respuesta deben ser aseguradas- envía el nombre de usuario y la contraseña en texto sin formato y recibe el token de acceso y el token secreto. Una vez que se ha configurado el par de tokens de acceso, si el intercambio inicial de tokens fue a través del modelo oAuth o el modelo xAuth es irrelevante tanto para el cliente como para el servidor durante el resto de la sesión. Esto tiene la ventaja de que puede aprovechar la infraestructura existente de oAuth y tener casi la misma implementación para aplicaciones móviles / web / de escritorio. La principal desventaja es que la aplicación tiene acceso al nombre de usuario y contraseña del cliente, pero parece que sus requisitos exigen este enfoque.

En cualquier caso, me gustaría estar de acuerdo con su intuición y la de varios otros respondedores aquí: no intente construir algo nuevo desde cero. Los protocolos de seguridad pueden ser fáciles de iniciar, pero siempre son difíciles de hacer bien, y cuanto más intrincados se vuelven, es menos probable que sus desarrolladores externos puedan implementarlos. Su protocolo hipotético es muy similar a o (x) Auth - api_key / api_secret, nonce, sha1 hashing - pero en lugar de poder usar una de las muchas bibliotecas existentes que sus desarrolladores necesitarán para crear sus propias bibliotecas.

lantius
fuente
2
También debo señalar que parece que el punto final 'token request token' va a estar en oAuth 2, aparece en el borrador actual como el tipo de concesión de acceso "contraseña". Consulte la sección 4.1.2: tools.ietf.org/html/draft-ietf-oauth-v2-10#section-4.1.2
lantius
Como le mencioné a Lonra, estoy investigando más sobre xAuth y específicamente por las razones que mencionó al final ... los desarrolladores pueden "usar las herramientas / libs de la plataforma" para interactuar con mi API, lo cual es "algo bueno" .
jsuggs
6

Súper tarde a la fiesta, pero quería agregar algunos puntos adicionales a tener en cuenta para cualquier persona interesada en este tema. Trabajo para una empresa que hace soluciones de seguridad de API móvil ( aproov ), por lo que toda esta área es definitivamente relevante para mis intereses.

Para empezar, lo más importante a tener en cuenta al tratar de proteger una API móvil es cuánto vale para usted . La solución correcta para un banco es diferente a la solución correcta para alguien que solo hace cosas por diversión.

En la solución propuesta, usted menciona que se requerirá un mínimo de tres parámetros:

  • apikey - entregado al desarrollador al registrarse
  • marca de tiempo: funciona como un identificador único para cada mensaje para una apikey dada
  • hash: un hash de la marca de tiempo + el apisecreto

La implicación de esto es que para algunas llamadas API no se requiere nombre de usuario / contraseña. Esto puede ser útil para aplicaciones en las que no desea forzar un inicio de sesión (por ejemplo, navegar en tiendas en línea).

Este es un problema ligeramente diferente al de la autenticación de usuario y se parece más a la autenticación o certificación del software. No hay usuario, pero aún así desea asegurarse de que no haya acceso malicioso a su API. Por lo tanto, utiliza su secreto API para firmar el tráfico e identificar el código que accede a la API como genuino. El problema potencial con esta solución es que luego tiene que revelar el secreto dentro de cada versión de la aplicación. Si alguien puede extraer el secreto, puede usar su API, suplantando su software pero haciendo lo que quiera.

Para contrarrestar esa amenaza, hay muchas cosas que puede hacer dependiendo de cuán valiosos sean los datos. Ofuscación es una forma sencilla de dificultar la extracción del secreto. Hay herramientas que harán eso por usted, más aún para Android, pero aún debe tener un código que genere su hash y una persona lo suficientemente capacitada siempre puede llamar a la función que hace el hash directamente.

Otra forma de mitigar el uso excesivo de una API que no requiere un inicio de sesión es limitar el tráfico e identificar y bloquear potencialmente las direcciones IP sospechosas. La cantidad de esfuerzo que desee realizar dependerá en gran medida de cuán valiosos sean sus datos.

Más allá de eso, puede comenzar fácilmente a ingresar al dominio de mi trabajo diario. De todos modos, es otro aspecto de la seguridad de las API que creo que es importante y que quería marcar.

El pragmático
fuente