Buscando comentarios sobre mi diseño de autenticación de API de estilo REST y la autenticación de dos factores

3

Autenticación

La autenticación va a estar basada en firmas. La firma se generará utilizando:

HMAC_SHA256(SHA1(secret_key) + '#' + request_data + '#' + utc_timestamp)

El utc_timestamp también se incluirá en el encabezado X-Timestamp o en la URL usando el parámetro _timestamp . El request_data contendrá todos los parámetros (URL y POST) y los encabezados relacionados con API. Todos estos datos se escriben en minúsculas, se ordenan y se unen utilizando = y & .

En general, la API REST admitirá tres esquemas de autenticación:

  1. Basado en claves de API : la firma incluirá la clave secreta especial asociada con la clave de API en particular. Utilizará el encabezado X-API-Key o el parámetro URL _api_key y el encabezado X-API-Signature o el parámetro URL _signature .

  2. Credenciales de usuario : la firma utilizará la contraseña como clave secreta. Utilizará el encabezado X-API-Signature o el parámetro URL _signature y el parámetro URL staff_name .

  3. Sesión : la firma usará la contraseña del usuario o un código generado especialmente (más detalles a continuación) ... Utilizará el encabezado X-Session-Signature o el parámetro _session_signature URL y el encabezado X-Session-ID o el parámetro _session_id URL.

Tenga en cuenta que la especificación permite utilizar Credenciales de usuario y Sesión al mismo tiempo (es decir, sus encabezados y parámetros no entran en conflicto).

Sesión

Descargo de responsabilidad: Sí, ya sé, las sesiones no son REST completas, ya que son de estado ... Sin embargo, nuestro producto requiere sesiones para cierta funcionalidad, y en aras de la simplicidad queremos mantener una API / protocolo - así que no estoy buscando entrar en un debate RESTful. Prefiero pensar que una sesión es solo un recurso, que debe mantener mientras trabaja con la API.

La sesión será solo un recurso: /api/v1/session .

Por lo tanto, el procedimiento estándar:

  1. Para crear una sesión, una aplicación cliente deberá enviar una solicitud POST utilizando la autenticación credenciales del personal , que se ha descrito anteriormente: POST /api/v1/session&staff_name=s-andy .

  2. El servidor responderá con 201 Created y entregará el ID de sesión en el cuerpo de la respuesta.

  3. Posteriormente, la aplicación cliente usará este ID de sesión y la contraseña del personal para acceder a la API.

Tras recibir una solicitud con un ID de sesión particular, el servidor actualizará la última hora de acceso del recurso de sesión apropiado.

Pero nuestros usuarios también tendrán la opción de usar la autenticación de dos factores. En la IU después de iniciar sesión, se solicitará a dichos usuarios que ingresen el código de verificación , que llegará a sus dispositivos móviles. Al diseñar la autenticación, pensé que sería genial tener un 'secreto' especial en lugar de la contraseña de usuario para la sesión. Entonces me di cuenta, ¿por qué no usar este código de verificación ?

Por lo tanto, el flujo para la autenticación de dos factores será:

  1. Una aplicación cliente envía una solicitud POST utilizando las credenciales del personal .

  2. El servidor inicia la generación y entrega de código de verificación y devuelve 202 Accepted con el ID de sesión, pero la sesión aún no se ha verificado.

  3. En 30 segundos, la aplicación cliente envía cualquier solicitud utilizando el identificador de sesión en el encabezado X-Session-ID o en el parámetro _session_id URL y el código de verificación como el secreto para generar la firma.

  4. Tras recibir dicha solicitud, el servidor actualiza la sesión, la verifica y guarda el código de verificación (también conocida como contraseña de un solo uso) como clave secreta para esta sesión.

  5. Luego, la aplicación cliente usará el id de sesión y el código de verificación (como clave secreta) para acceder a la API.

  6. Cuando se agota el tiempo de la sesión y, por lo tanto, se elimina, o cuando el usuario elimina la sesión (es decir, realiza el cierre de sesión), el ID de la sesión y la "contraseña de un solo uso" se vuelven inutilizables.

Quería usar esta comunidad de expertos como una caja de resonancia para esta idea de autenticación de dos factores; ¿Puedes ver alguna trampa que yo no pueda?

    
pregunta Andriy Lesyuk 14.03.2013 - 20:43
fuente

3 respuestas

5

El problema es que lo que llamas código de verificación es en realidad un token de dos factores. Los tokens son normalmente cortos (como 6-7 caracteres).

Así lo harás:

HMAC_SHA256(SHA1(TOKEN) + '#' + request_data + '#' + utc_timestamp)

Y el token es demasiado corto.

Tampoco entiendo cómo se ve un session_id verificado frente a un session_id no verificado.

El mayor problema es que este esquema de autenticación parece innecesariamente complejo. La complejidad y la seguridad son en su mayoría opuestos.

La forma en que sugerimos a nuestros clientes ( www.authy.com ) hacer la autenticación de dos factores es:

  1. El usuario envía una solicitud POST con sus credenciales.

  2. El servidor valida las credenciales. Si es correcto, el servidor genera una cadena de 256 bits y la guarda en la base de datos, así como la devuelve al navegador. Llamaremos a esta cadena "otp".

  3. Se solicita al usuario su token de dos factores.

  4. En la solicitud POST del paso 3 habrá:

    hidden_field otp - > que vuelve del servidor en el paso 2
       campo de texto token de dos factores - > que la persona obtiene de su aplicación móvil

  5. En este punto de su servidor, verifica el token de dos factores y el otp. Si te corrijo cree la sesión y restablezca el otp para que no pueda volver a utilizarse.

respondido por el daniel 15.03.2013 - 00:37
fuente
1

Su código de verificación se parece mucho a la forma verificación en dos pasos de Google trabaja. Si es lo suficientemente seguro para las cuentas de Google, entonces probablemente funcione para usted.

¿La pregunta importante es cómo va a entregar el código de verificación al usuario? Es el problema clásico de los sistemas de codificación de llaves simétricas.

Además, ¿en qué lenguaje estás implementando esto? ¿Has encontrado alguna solución de código abierto que haga este tipo de cosas? Por ejemplo, en c # ServiceStack Auth hace el mismo tipo de cosas con credenciales y sesiones.

    
respondido por el kampsj 15.03.2013 - 00:13
fuente
-1

¿Es importante preguntarse si realmente necesita usar las sesiones? ¿Qué vas a almacenar en la sesión? ¿Tiene algún dato de contexto del cliente (por ejemplo, carrito de compras) que no represente un objeto de dominio que se persista? Si es así, un servicio RESTful probablemente no sea la mejor opción. Si no, en lugar de almacenar el contexto del cliente en el servidor, ¿puede almacenarlo en el cliente?

Con eso en mente, me gustaría ir con la opción 2 (credenciales de usuario). Perdón por publicar esta misma respuesta para varias preguntas, pero uno de estos enfoques es el siguiente:

Digamos que tienes una aplicación de cliente móvil; primero haga que el usuario se registre o se registre proporcionando su correo electrónico (nombre de usuario) y contraseña en un formulario web separado (que no forma parte de la aplicación o del servicio REST). Luego, al registrarse con éxito, responde con una clave de usuario (puede ser una clave secreta compartida almacenada en la cuenta de usuario en el servidor (base de datos) para el cifrado simétrico o una clave pública para el cifrado asimétrico donde la clave privada correspondiente se almacena en la cuenta del usuario). en el servidor (base de datos). Todo esto se hace mediante un formulario web sobre SSL.

Ahora, cuando el usuario abre la aplicación cliente, debe solicitar sus credenciales que se enviarán con cada solicitud al servicio RESTful. Deben proporcionar su nombre, contraseña y clave de cifrado que recibieron previamente. Esto solo debe hacerse una vez. La aplicación luego proporciona un encabezado http con cada solicitud que se parece a esto:

AUTENTIFICAR > nombre de usuario: marca de hora: cifrada {contraseña: marca de hora} / AUTHENTICATE >

Tenga en cuenta que tanto la contraseña como la marca de tiempo dentro de {} se cifran con la clave del usuario. La marca de tiempo se actualiza con cada solicitud única.

Implemente un filtro de autenticación en el servidor que haga lo siguiente:

Primero verifique la marca de tiempo y si está vencida (digamos, más de 1 segundo) envíe un código de respuesta HTTP NO AUTORIZADO. Si la marca de tiempo es válida, busque el nombre de usuario en la base de datos de su cuenta de usuario. Si no se encuentra, envíe una respuesta HTTP NO AUTORIZADA. Si se encuentra el nombre de usuario, busque la clave de cifrado almacenada para ese usuario (recuerde que esto puede ser una clave secreta compartida o la clave privada para la clave pública de los usuarios). Descifre el cifrado {contraseña: marca de tiempo}. La contraseña descifrada debe coincidir con la contraseña de los usuarios almacenada en su base de datos (la contraseña también podría estar incrustada en la base de datos usando otra clave para mayor seguridad) y la marca de tiempo descifrada también debe coincidir con la marca de tiempo no cifrada enviada en el encabezado AUTENTICADO arriba. De lo contrario, envíe un código de respuesta HTTP NO AUTORIZADO. Si tiene éxito, la solicitud se ha autenticado sin el uso de cookies / sesiones.

También puede almacenar en caché los detalles del usuario para evitar realizar una búsqueda en la base de datos con cada solicitud. Además, puede usar la misma clave para cifrar cualquier información confidencial que se envíe de vuelta al cliente en la respuesta y marcarla para que el cliente sepa descifrarla.

Ahora, si alguien está indagando e intercepta la solicitud, no podrá reutilizarla para obtener acceso porque la marca de tiempo no será válida o, si actualizan la marca de hora sin cifrar para que sea válida, no coincidirá con la encriptada marca de tiempo (después de que el filtro de autenticación lo descifre).

Otra ventaja de este enfoque sobre el uso de una sola clave de aplicación es que ahora tiene un control completo sobre quién puede acceder a su servicio al poner una fecha de vencimiento en la cuenta de usuario en la base de datos (implementando efectivamente un servicio basado en suscripción). Esto es genial porque al principio es posible que desee obtener la mayor cantidad de usuarios posible con una suscripción de prueba (gratis por un año, por ejemplo) y luego bloquear el acceso a ese usuario si no ha realizado el pago para extender la fecha de vencimiento de la cuenta:)

    
respondido por el user3598746 10.05.2014 - 10:57
fuente

Lea otras preguntas en las etiquetas