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 a mi primera pregunta:
¿Es esto realmente mejor que simplemente exigir a los clientes que utilicen la autenticación básica HTTP en cada solicitud y el almacenamiento en caché de las llamadas al servicio del servidor de autenticación?
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 puedan comenzar las solicitudes de contenido. Los tokens pueden ser potencialmente más flexibles en su alcance (es decir, solo otorgar 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.
Actualmente los tokens se adquieren así:
curl -X POST localhost/token --data "api_key=81169d80...
&verifier=2f5ae51a...
×tamp=1234567
&user=foo
&pass=bar"
El api_key
, timestamp
y verifier
son requeridos por todas las solicitudes. El "verificador" es devuelto por:
sha1(timestamp + api_key + shared_secret)
Mi intención es permitir solo llamadas de partes conocidas y evitar que las llamadas se reutilicen textualmente.
¿Es esto lo suficientemente bueno? Underkill? ¿Exceso?
Con un token en la mano, los clientes pueden adquirir recursos:
curl localhost/posts?api_key=81169d80...
&verifier=81169d80...
&token=9fUyas64...
×tamp=1234567
Para la llamada más simple posible, esto parece horriblemente detallado. Teniendo en cuenta que la aplicación shared_secret
terminará incrustada (como mínimo) en una aplicación de iOS, de la cual supongo que se puede extraer, ¿esto incluso ofrece algo más allá de una falsa sensación de seguridad?
fuente
Respuestas:
Permítanme separar todo y resolver cada problema de forma aislada:
Autenticación
Para la autenticación, baseauth tiene la ventaja de que es una solución madura a nivel de protocolo. Esto significa que muchos de los problemas de "podría surgir más tarde" ya están resueltos para usted. Por ejemplo, con BaseAuth, los agentes de usuario saben que la contraseña es una contraseña, por lo que no la almacenan en caché.
Carga del servidor de autenticación
Si distribuye un token al usuario en lugar de almacenar en caché la autenticación en su servidor, aún está haciendo lo mismo: almacenar en caché la información de autenticación. La única diferencia es que usted está transfiriendo la responsabilidad del almacenamiento en caché al usuario. Esto parece una mano de obra innecesaria para el usuario sin ganancias, por lo que recomiendo manejar esto de manera transparente en su servidor como lo sugirió.
Seguridad de transmisión
Si puede usar una conexión SSL, eso es todo, 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.
Si eso no es posible, y la información transmitida no es secreta, recomiendo asegurar la solicitud con un hash, como sugirió en el enfoque de token. Dado que el hash proporciona la seguridad, puede indicar a sus usuarios que proporcionen el hash como contraseña de baseauth. Para mejorar la robustez, recomiendo usar una cadena aleatoria en lugar de la marca de tiempo como "nonce" para evitar ataques de repetición (se pueden hacer dos solicitudes legítimas durante el mismo segundo). En lugar de proporcionar campos separados de "secreto compartido" y "clave de API", simplemente puede usar la clave de API como secreto compartido, y luego usar una sal que no cambie para evitar ataques de la mesa arcoiris. El campo de nombre de usuario también parece un buen lugar para poner el nonce, ya que es parte de la autenticación. Así que ahora tienes una llamada limpia como esta:
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, considero que el nivel de seguridad apropiado (just-right-kill).
Almacenamiento secreto seguro
Depende de a quién intentes frustrar. Si está evitando 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 llavero en el sistema operativo de destino y que el SDK (o el implementador) almacene el Clave 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 enfoque de cifrar y almacenar por separado casi funciona. Esta es una criptografía de caja blanca, y hasta la fecha, nadie ha encontrado una solución verdaderamente segura para los problemas de esta clase. Lo menos que puede hacer es emitir una sola clave para cada usuario para que pueda prohibir las claves abusadas.
(*) EDITAR: las conexiones SSL ya no deberían considerarse seguras sin tomar medidas adicionales para verificarlas .
fuente
Una API RESTful pura debe usar las características estándar del protocolo subyacente:
Para HTTP, la API RESTful debe cumplir con los encabezados estándar HTTP existentes. Agregar un nuevo encabezado HTTP viola los principios REST. No vuelva a inventar la rueda, use todas las características estándar en los estándares HTTP / 1.1, incluidos los códigos de respuesta de estado, encabezados, etc. Los servicios web RESTFul deberían aprovechar y confiar en los estándares HTTP.
Los servicios RESTful DEBEN ESTAR SIN ESTADO. Cualquier truco, como la autenticación basada en token que intenta recordar el estado de las solicitudes REST anteriores en el servidor, viola los principios REST. De nuevo, esto es IMPRESCINDIBLE; es decir, si su servidor web guarda cualquier información relacionada con el contexto de solicitud / respuesta en el servidor en un intento de establecer cualquier tipo de sesión en el servidor, entonces su servicio web NO tiene estado. Y si NO es apátrida NO es RESTFul.
En pocas palabras: para fines de autenticación / autorización, debe usar el encabezado de autorización estándar HTTP. Es decir, debe agregar el encabezado de autenticación / autorizació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 de cómo se debe formatear este encabezado se definen en los estándares RFC 2616 HTTP 1.1 - sección 14.8 Autorización de RFC 2616, y en la RFC 2617 Autenticación HTTP: Autenticación de acceso básica y resumida .
Desarrollé un servicio RESTful para la aplicación Cisco Prime Performance Manager. Busque en Google el documento de REST API que escribí para esa aplicación para obtener más detalles sobre el cumplimiento de RESTFul API aquí . En esa implementación, he optado por utilizar el esquema de autorización HTTP "Básico". - consulte la versión 1.5 o superior de ese documento REST API y busque autorización en el documento.
fuente
En la web, un protocolo con estado se basa en tener un token temporal que se intercambia entre un navegador y un servidor (a través del encabezado de cookies 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 ESTATUA. Y si la conversación entre el cliente y el servidor es ESTATUA, no es RESTANTE.
El nombre de usuario / contraseña (enviado en el encabezado de Autorización) generalmente se conserva en la base de datos con la intención de identificar a un usuario. A veces, el usuario puede significar otra aplicación; sin embargo, el nombre de usuario / contraseña NUNCA tiene 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 la Autorización (después de la Autorización Básica de HTTP) NO TIENE NINGÚN TIPO porque el front-end del servidor web no está creando ni manteniendo ninguna información ESTATALen nombre de un agente de usuario de cliente web específico. Y según mi comprensión de REST, el protocolo establece claramente que la conversación entre los clientes y el servidor debe ser ESTÁTICA. Por lo tanto, si queremos tener un verdadero servicio RESTful, debemos usar el nombre de usuario / contraseña (consulte RFC mencionado en mi publicación anterior) en el encabezado de autorización para cada llamada, NO un token de tipo sensorial (por ejemplo, tokens de sesión creados en servidores web , Tokens OAuth creados en servidores de autorización, etc.).
Entiendo que varios proveedores llamados REST están usando tokens como OAuth1 u OAuth2 accept-tokens para ser pasados como "Autorización: Portador" en los encabezados HTTP. Sin embargo, me parece que el uso de esos tokens para servicios RESTful violaría el verdadero significado SIN ESTADO que REST abarca; porque esos tokens son datos temporales creados / mantenidos en el lado del servidor para identificar un agente de usuario de cliente web específico durante la duración válida de una conversación de ese cliente / servidor web. Por lo tanto, cualquier servicio que esté utilizando esos tokens OAuth1 / 2 no debe llamarse REST si queremos mantener el VERDADERO significado de un protocolo STATELESS.
Rubens
fuente