2012-03-19 19 views
106

Estoy desarrollando una API REST que requiere autenticación. Debido a que la autenticación en sí ocurre a través de un servicio web externo a través de HTTP, razoné que distribuiríamos tokens para evitar llamar repetidamente al servicio de autenticación. Lo que me lleva claramente a mi primera pregunta:API REST Autenticación basada en token

¿Es esto realmente mejor que solo requerir que los clientes utilicen HTTP Basic Auth en cada solicitud y que el caché llame al servicio de autenticación del servidor?

La solución de autenticación básica tiene la ventaja de no requerir un viaje de ida y vuelta completo al servidor antes de que las solicitudes de contenido puedan comenzar. Los tokens pueden ser potencialmente más flexibles en su alcance (es decir, solo otorgan derechos a recursos o acciones particulares), pero eso parece más apropiado para el contexto de OAuth que mi caso de uso más simple.

fichas Actualmente se adquieren de esta manera:

curl -X POST localhost/token --data "api_key=81169d80... 
            &verifier=2f5ae51a... 
            &timestamp=1234567 
            &user=foo 
            &pass=bar" 

El api_key, timestamp y verifier son requeridos por todas las solicitudes. El "verificador" se devuelve por:

sha1(timestamp + api_key + shared_secret) 

Mi intención es permitir sólo llamadas de personas conocidas, y para evitar las llamadas de ser reutilizados pie de la letra.

¿Es esto lo suficientemente bueno? Underkill? ¿Overkill?

con una ficha en la mano, los clientes pueden adquirir recursos:

curl localhost/posts?api_key=81169d80... 
        &verifier=81169d80... 
        &token=9fUyas64... 
        &timestamp=1234567 

Para la llamada más simple posible, esto parece algo terriblemente detallado. Teniendo en cuenta que el shared_secret terminará siendo incrustado (como mínimo) en una aplicación de iOS, de la que asumiría que se puede extraer, ¿esto ofrece algo más allá de una falsa sensación de seguridad?

+2

En lugar de utilizar SHA1 (marca de tiempo api_key + + shard_secret) se debe utilizar HMAC (shared_secret, timpestamp + api_key) para una mejor seguridad de hash http://en.wikipedia.org/wiki/Hash- based_message_authentication_code –

+0

@ MiguelA.Carrasco Y parece ser el consenso en 2017 que bCrypt es la nueva herramienta de hash. –

Respuesta

87

Let Me separadas a todo y resolver enfoque de cada problema de forma aislada:

autenticación

Para la autenticación, baseauth tiene la ventaja de que es una solución madura en el nivel de protocolo. Esto significa que muchos de los "podrían aparecer más tarde" los problemas ya están resueltos para usted. Por ejemplo, con BaseAuth, los agentes de usuario saben que la contraseña es una contraseña para que no la almacenen en caché.

carga del servidor de autenticación

Si dispensar un valor al usuario en lugar de almacenamiento en caché de la autenticación en el servidor, usted todavía está haciendo lo mismo: Almacenamiento en caché de la información de autenticación. La única diferencia es que está transfiriendo la responsabilidad del almacenamiento en caché al usuario. Esto parece una tarea innecesaria para el usuario sin ganancias, por lo que recomiendo manejar esto de forma transparente en su servidor como lo sugirió.

Transmisión de Seguridad

Si puede usar una conexión SSL, eso es todo lo que hay que hacer, la conexión es segura *.Para evitar la ejecución múltiple accidental, puede filtrar varias URL o solicitar a los usuarios que incluyan un componente aleatorio ("nonce") en la URL.

url = username:[email protected]/api/call/nonce 

Si eso no es posible, y la información transmitida no es secreta, recomiendo asegurar la solicitud con un hash, como usted sugiere en el enfoque simbólico. Como el hash proporciona la seguridad, puede indicar a los usuarios que proporcionen el hash como contraseña de baseauth. Para una mayor robustez, recomiendo usar una cadena aleatoria en lugar de la marca de tiempo como "nonce" para evitar ataques de repetición (se podrían realizar dos solicitudes legítimas durante el mismo segundo). En lugar de proporcionar campos separados de "secreto compartido" y "clave de la API", puede simplemente usar la clave api como secreto compartido, y luego usar una sal que no cambia para evitar ataques de tabla de arco iris. El campo de nombre de usuario parece ser un buen lugar para poner el nonce también, ya que es parte de la autenticación. Así que ahora tiene una llamada limpia como esta:

nonce = generate_secure_password(length: 16); 
one_time_key = nonce + '-' + sha1(nonce+salt+shared_key); 
url = username:[email protected]/api/call 

Es cierto que esto es un poco laborioso. Esto se debe a que no está utilizando una solución de nivel de protocolo (como SSL). Por lo tanto, podría ser una buena idea proporcionar algún tipo de SDK a los usuarios para que al menos no tengan que pasar por ellos mismos. Si necesita hacerlo de esta manera, me parece apropiado el nivel de seguridad (just-right-kill).

escondite seguro

Depende de quién usted está tratando de frustrar. Si impide que las personas con acceso al teléfono del usuario utilicen su servicio REST a nombre del usuario, sería una buena idea encontrar algún tipo de API de keyring en el sistema operativo de destino y tener el SDK (o el implementador) almacenando el llave allí. Si eso no es posible, al menos puede hacer que sea un poco más difícil obtener el secreto cifrándolo y almacenando los datos cifrados y la clave de cifrado en lugares separados.

Si está tratando de evitar que otros proveedores de software obtengan su clave API para evitar el desarrollo de clientes alternativos, solo el método de encriptación y almacenamiento por separado funciona casi. Esta es la criptografía de whitebox, y hasta la fecha, nadie ha encontrado una solución verdaderamente segura para los problemas de esta clase. Lo mínimo que puede hacer es emitir una sola clave para cada usuario para que pueda prohibir las claves abusadas.

(*) EDIT:conexiones SSL should no longer be considered secure sin taking additional steps to verify ellos.

+0

Gracias cmc, todos buenos puntos y gran alimento para el pensamiento.Terminé tomando un enfoque token/HMAC similar al que discutiste anteriormente, como el mecanismo de autenticación [S3 REST API] (http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html) . – cantlin

+0

Si almacena en caché el token en el servidor, ¿no es esencialmente el mismo que el ID de sesión anterior? La identificación de la sesión es efímera y también se adjunta al almacenamiento rápido de la memoria caché (si la implementa) para evitar golpear su base de datos en cada solicitud. El verdadero diseño RESTful y sin estado no debe tener sesiones, pero si está utilizando un token como ID y luego sigue presionando el DB, entonces ¿no sería mejor usar ID de sesión en su lugar? Alternativamente, puede buscar tokens web JSON que contengan información encriptada o firmada para datos de sesión completos para un verdadero diseño sin estado. – JustAMartin

15

Una API REST pura debe utilizar las funciones estándar de protocolo subyacentes:

  1. Para HTTP, la API REST debe cumplir con las cabeceras HTTP estándar existentes. Agregar un nuevo encabezado HTTP viola los principios REST. No vuelva a inventar la rueda, use todas las funciones estándar en los estándares HTTP/1.1, incluidos los códigos de respuesta de estado, los encabezados, etc. Los servicios web RESTFul deben aprovechar y confiar en los estándares HTTP.

  2. Los servicios RESTANTES DEBEN ser ESTATUILLOS. Cualquier truco, como la autenticación basada en token que intenta recordar el estado de las solicitudes de REST previas en el servidor, infringe los principios de REST. Nuevamente, este es un DEBER; es decir, si su servidor web guarda cualquier información relacionada con el contexto de solicitud/respuesta en el servidor para intentar establecer cualquier clase de sesión en el servidor, entonces su servicio web NO es sin estado. Y si NO es apátrida, NO ES RESTFul.

Conclusión: Para fines de autenticación/autorización, debe utilizar el encabezado de autorización HTTP estándar. Es decir, debe agregar el encabezado de autorización/autenticación HTTP en cada solicitud posterior que deba autenticarse. La API REST debe seguir los estándares del Esquema de Autenticación HTTP. Los detalles sobre cómo debe formatearse este encabezado están definidos en los estándares RFC 2616 HTTP 1.1 - sección 14.8 Autorización del RFC 2616, y en el RFC 2617 Autenticación HTTP: Autenticación Básica y del Acceso al Resumen .

He desarrollado un servicio RESTful para la aplicación Cisco Prime Performance Manager. Busque en Google el documento REST API que escribí para esa aplicación para obtener más detalles sobre el cumplimiento API RESTFul here. En esa implementación, he elegido utilizar el esquema de Autorización HTTP "Básico". - verifique la versión 1.5 o superior de ese documento REST API, y busque la autorización en el documento.

+5

_ "Agregar un nuevo encabezado HTTP viola los principios de REST" _ ¿Cómo? Y si lo hace, puede ser tan amable de explicar cuál es exactamente la diferencia (en relación con los principios) entre una contraseña que caduca después de un cierto período y un token que caduca después de un cierto período. – zeroflagL

+0

En la web, un protocolo con estado se basa en la creación de un token que se intercambia entre un navegador y un servidor (mediante el encabezado de la cookie o la reescritura de URI) en cada solicitud. Ese token se crea/mantiene en el lado del servidor; es decir, el token tiene un tiempo de vida, es específico para la duración de la interacción cliente-servidor, y así sucesivamente. –

+4

El nombre de usuario + contraseña es un token (!) Que se intercambia entre un cliente y un servidor en cada solicitud. Ese token se mantiene en el servidor y tiene un tiempo de vida. Si la contraseña expira, debo adquirir una nueva. Pareces asociar "token" con "sesión de servidor", pero esa es una conclusión inválida. Incluso es irrelevante porque sería un detalle de implementación. Su clasificación de tokens que no sea el nombre de usuario/contraseña como con estado es puramente artificial, yo. – zeroflagL

2

En la web, un protocolo con estado se basa en tener un token temporal que se intercambia entre un navegador y un servidor (mediante el encabezado de la cookie o la reescritura de URI) en cada solicitud. Ese token generalmente se crea en el extremo del servidor, y es una pieza de datos opacos que tiene un cierto tiempo de vida, y tiene el único propósito de identificar un agente de usuario web específico. Es decir, el token es temporal y se convierte en un ESTADO que el servidor web debe mantener en nombre de un agente de usuario cliente durante la duración de esa conversación. Por lo tanto, la comunicación que usa un token de esta manera es ESTATAL. Y si la conversación entre el cliente y el servidor es ESTATAL, no es RESTful.

El nombre de usuario/contraseña (enviado en el encabezado Autorización) generalmente persiste en la base de datos con la intención de identificar a un usuario. A veces, el usuario podría significar otra aplicación; sin embargo, el nombre de usuario/contraseña es NUNCA con la intención de identificar un agente de usuario de cliente web específico. La conversación entre un agente web y un servidor basada en el uso del nombre de usuario/contraseña en el encabezado de Autorización (siguiendo la Autorización Básica HTTP) es APÁTRIDA porque el front-end del servidor web no está creando o manteniendo ningún estado información en nombre de un agente de usuario del cliente web específico. Y en base a mi comprensión de REST, el protocolo establece claramente que la conversación entre los clientes y el servidor debe ser APÁTRIDA. Por lo tanto, si queremos tener un verdadero servicio RESTful, deberíamos usar el nombre de usuario/contraseña (Referir a RFC mencionado en mi publicación anterior) en el encabezado Authorization para cada llamada, NO un tipo de token de sensión (por ejemplo, tokens de sesión creados en servidores web , Tokens OAuth creados en servidores de autorización, etc.).

Entiendo que varios proveedores de REST llamados están utilizando tokens como OAuth1 u OAuth2 accept-tokens para pasarlos como "Autorización: Portador" en encabezados HTTP. Sin embargo, me parece que usar esos tokens para servicios RESTful violaría el verdadero STATELESS que significa que REST lo abarca; porque esos tokens son temporal datos creados/mantenidos por el lado del servidor para identificar un agente de usuario de cliente web específico para la duración válida de una conversación de cliente/servidor web. Por lo tanto, cualquier servicio que use esos tokens OAuth1/2 no se debe llamar REST si queremos adherirnos al VERDADERO significado de un protocolo STATELESS.

Rubens