http - por - web api rest



Cómo hacer la autenticación con una API REST ¿verdad?(Navegador+clientes nativos) (0)

Estoy construyendo una aplicación web usando Rails. En este momento estoy usando Devise con sesiones HTTP, que fue bastante fácil de configurar y funciona bien.

La aplicación consiste en una URL que proporciona una aplicación web AJAX. El resto de las URL disponibles pertenecen a la API REST. Entonces, todo y cada pequeña solicitud de datos se hace a través de AJAX.

Ahora me gustaría extender todo para apoyar a los clientes nativos. He leído mucho sobre auth stateless, http basic y digest auth, http sessions, cookies, xsrf, etc. ... Y ahora siento que no puedo tener una aplicación segura, porque siempre hay una forma de secuestrar algunas partes de ella .

1 .: sesión de HTTP vs. token de autenticación sin estado

¿Cual es la diferencia? No lo entiendo

  • Sesión HTTP:

    1. El cliente solicita una URL (primera solicitud al servidor)
    2. El servidor da la respuesta normal más alguna cadena única (== ID de sesión)
    3. El cliente debe enviar esta cadena con cada solicitud (lo que se hace automáticamente usando el encabezado HTTP)
    4. El cliente inicia sesión en -> El servidor memoriza que esta identificación de sesión en particular ya está registrada
    5. El cliente visita una página que requiere auth -> Nada especial que hacer, porque la ID de la sesión se enviará automáticamente al servidor a través del encabezado HTTP
  • token de autenticación sin estado:

    1. URL de solicitud del cliente (primera solicitud al servidor)
    2. El servidor simplemente da la respuesta normal sin ninguna clave o token o identificación
    3. (nada especial aquí)
    4. El cliente inicia sesión en -> El servidor crea un token de autenticación y envía este token al cliente dentro de la respuesta
    5. Página de visitas del cliente que requiere auth -> El cliente debe enviar el token de autenticación

Para mí, ambas formas se ven bastante similares. Con Rails también puedo elegir almacenar la sesión dentro de la base de datos ... Devise haría lo mismo con el token de autenticación sin estado.

2 .: El método de autenticación

En este momento estoy usando POST /users/sign_in con {"user":{"email":"[email protected]","password":"p455w0rd"}} .

Pero hay otras posibilidades como HTTP auth aut y HTTP digest auth, pero también soluciones como oAuth (demasiado grande para mi propósito).

Por lo que he leído:

  • Con respecto a la seguridad de sign_in , no hay diferencia entre POST /users/sign_in y HTTP auth auth. Ambos usan texto claro.
  • Para sign_out HTTP auth auth tiene una desventaja: Cerrar sesión solo es posible cerrando la ventana del navegador
  • HTTP digest auth tiene una gran ventaja: no transmite la contraseña en absoluto (solo un hash de contraseña más cadena generada al azar)
  • (Alemán) Wikipedia dice: HTTP digest auth no es compatible con todos los navegadores. Tal vez esta información es demasiado antigua?

Lo que necesito:

  • nombres de usuario y contraseñas hash (bcrypt) almacenados en una base de datos.
  • el usuario puede cambiar su contraseña y la contraseña no debe enviarse en texto sin formato. (El mismo problema ocurre cuando se trata de usuario sign_up). ¿Soluciones posibles?
    1. por supuesto: usando SSL / TLS
    2. el cliente solicita un want_to_change_password_salt y lo usa para cifrar la contraseña en el lado del cliente. pero (?!) de esta manera envié una parte esencial de la contraseña hash a través del cable más la contraseña hash. Suena inseguro para mí?

3 .: Token de CSRF

Como dije anteriormente, ahora solo tengo un sitio web AJAX normal usando la API REST. Tiene protección XSRF: el sitio web se entrega mediante raíles y, por lo tanto, tiene incrustado el token XSRF. Lo leí usando AJAX y lo transmití cuando hacía un POST . Rails luego devuelve los datos solicitados y un nuevo token XSRF, que luego uso para el próximo POST .

Ahora quiero cambiar mi aplicación de servidor para que funcione con clientes nativos. Un cliente nativo no cargará la página HTML y, por lo tanto, no recuperará un token CSRF. Así que las siguientes opciones vinieron a mi mente:

  • Crear un recurso REST de token XSRF. Por lo tanto, el cliente (nativo) debe solicitar un token XSRF desde este recurso antes de que pueda hacer el primer POST .
  • Deshabilite la protección XSRF por completo.

Preguntas:

  • ¿Cómo funciona la protección XSRF (en Rails)? ¿Cómo sabe el servidor qué token pertenece a qué cliente? La única forma en que puedo pensar son sesiones. Esta suposición conduce a:
  • Si desactivo la sesión para crear una API REST completamente sin estado, la protección XSRF ya no funcionará. ¿Derecha?

4 .: token de autenticación sin estado

Aquí tengo principalmente muchas preguntas:

  • ¿Tiene los mismos problemas de seguridad que las sesiones HTTP? Lo que quiero decir es que robar el ID de sesión tiene el mismo efecto que robar el token de autenticación. ¿Derecha?
  • La caducidad del token de autenticación debería funcionar de la misma manera que con las sesiones HTTP: el servidor debe almacenar en algún lugar (base de datos o sesión) una marca de tiempo y verificarlo.
  • sign_out funciona igual también?
    • Sesión: Destruye sesión en el servidor
    • Token de autenticación: destruir el token en el servidor
  • Por lo que he leído, debería ser más seguro almacenar el token de autenticación dentro del encabezado HTTP (como el ID de sesión), porque los registros del servidor pueden contener parámetros GET y, por lo tanto, podrían contener el token.
  • ¿Debería ser simplemente un token de autenticación simple o sería mejor si el cliente también transmite su user_id o incluso la contraseña hash? HERE leo que el cliente debe enviar:
    1. user_id
    2. expiration_date
    3. un hash (¿o qué es HMAC?) de [ user_id , SECRET_KEY , SECRET_KEY ]. Donde SECRET_KEY es básicamente una cadena aleatoria generada por el servidor.

Perdón por la publicación de Huuuge, pero la seguridad es esencial. Y no quiero cometer errores de diseño que probablemente podrían exponer datos privados.

Gracias :)

Aquí hay un poco de información nueva y nuevas preguntas ;-) :

5 .: Clientes nativos

En lo que respecta a los clientes nativos, no hay una forma ( fácil ) de usar las sesiones:

  • Un cliente nativo no es un navegador

  • Por lo tanto, no manejará fácilmente las cookies (y sin cookies no hay un manejo de sesión típico)

Entonces hay 3 elecciones posibles:

  1. Implementar el manejo de sesión para clientes nativos. Esto sería como:

    1. Iniciar sesión
    2. leer el encabezado HTTP de respuesta para obtener las cookies
    3. guarde todos los datos de cookies que necesita (especialmente el que contiene la sesión) localmente
    4. envíe esta identificación de sesión con cada solicitud que haga
  2. No use sesiones en absoluto. Desde el punto de vista de un cliente nativo, es prácticamente lo mismo que 1 .:

    1. Iniciar sesión
    2. Obtenga un token de autenticación del encabezado HTTP o cuerpo de respuesta (es su aplicación, aunque depende de usted)
    3. guarda este token localmente
    4. envía este token con cada solicitud
  3. El enfoque híbrido. Esto significa básicamente que el servidor tiene que distinguir entre el navegador y el cliente nativo y luego verificar los datos de sesión y de id. De sesión provistos o (para clientes nativos) verificar el token de autenticación proporcionado.

6 .: CSRF Token con autenticación sin estado (= sin sesión / sin cookies)

CSRF Protection protege a sus usuarios de sitios web maliciosos, que intentan hacer alguna solicitud en su API en nombre de su usuario conectado, pero sin que su usuario lo sepa. Eso es bastante simple cuando se usan sesiones:

  1. El usuario inicia sesión en su API
  2. Se crea una sesión
  3. El navegador de los usuarios tendrá un conjunto de cookies con esta ID de sesión
  4. Cada solicitud que realiza su usuario su API se autentica automáticamente, ya que el navegador enviará todas las cookies (incluida la id. De la sesión) junto con cada solicitud a su API.

Y, por lo tanto, el sitio web de ataque simplemente tiene que hacer lo siguiente:

  1. Escriba un <form> HTML personalizado que apunte a su API
  2. Permita que el usuario de alguna manera haga clic en el botón Submit

Por supuesto, esta forma será algo así como:

<form action="http://your.api.com/transferMoney" method="post">
  <input type="hidden" name="receiver" value="ownerOfTheEvilSite" />
  <input type="hidden" name="amount" value="1000.00" />
  <input type="submit" value="WIN MONEY!!" />
</form>

Esto lleva a las siguientes suposiciones :

  1. La protección CSRF solo es necesaria porque los navegadores envían automáticamente las cookies.

  2. Clientes nativos que no necesitan Protección CSRF (por supuesto: su navegador no puede acceder a los datos de autenticación (token, cookie, lo que sea) de su aplicación nativa, y su aplicación nativa no usará un navegador para comunicarse con la API)

  3. Si tiene un diseño de API que no utiliza cookies para autenticar al usuario, no hay posibilidad de hacer CSRF. Porque el atacante debe conocer el token de autenticación y enviarlo explícitamente junto con la solicitud maliciosa.

Si desea proteger su aplicación, puede, por supuesto, usar tokens CSRF junto con su mecanismo de autenticación sin estado, pero estoy bastante seguro de que no hay ganancia de seguridad adicional.

7 .: Los métodos HTTP adecuados para elegir

Iniciar sesión / Iniciar sesión y cerrar sesión / Cerrar sesión:

Nunca use GET por (al menos) tres razones:

  1. La protección CSRF en la mayoría de los casos solo protege POST, PUT, PATCH y DELETE y, por lo tanto, un CSRF podría iniciar sesión en un usuario sin su conocimiento al usar una solicitud GET

  2. Las solicitudes GET nunca deben cambiar el estado de la aplicación. Pero cuando, por ejemplo, al utilizar Sesiones, el estado de la aplicación cambia al iniciar sesión / cerrar sesión porque se crea o destruye una sesión.

  3. Cuando se utiliza una solicitud GET y se transmite la información de autenticación como parámetros de URL (es decir, http://your.api.com/login?username=foo&password=bar ), hay otro problema: ¡Registros del servidor! La mayoría de los servidores simplemente registran todas las solicitudes HTTP, incluidos todos los parámetros de URL. Eso significa que: si su servidor es pirateado, no es necesario descifrar los hash de las contraseñas de su base de datos, solo deben echarle un vistazo a los archivos de registro del servidor. Además, un administrador malicioso también podría leer la información de inicio de sesión para cada usuario. Soluciones:

    • Use POST (o el método que desee) y envíe la información de autenticación dentro del cuerpo de la solicitud. O:
    • Envíe la información de autenticación dentro de los encabezados HTTP. Porque esa información normalmente no aparece en los archivos de registro del servidor. O:
    • Eche un vistazo a la configuración del servidor y dígale que elimine todos los parámetros de URL que se denominan "contraseña" (u ofuscación es, por lo que la URL se convierte en login?username=foo&password=*** dentro de los registros). Pero sugiero que simplemente use el cuerpo de la solicitud para este tipo de información junto con el método POST.

Entonces podrías usar, por ejemplo:

POST http://your.api.com/authentication para iniciar sesión

DELETE http://your.api.com/authentication para cerrar la sesión

8 .: Contraseñas y Hashing

La autenticación solo funciona con alguna clave secreta. Y, por supuesto, esta clave debe mantenerse en secreto. Esto significa:

  • Nunca almacene una contraseña en texto plano en su base de datos. Hay varias bibliotecas disponibles para que sea seguro. En mi opinión, la mejor opción es bcrypt .

  • bcrypt : Ha sido optimizado para contraseñas hash. Genera automáticamente una sal y mezcla la contraseña varias veces (rondas). Además, el hash-string generado contiene todo lo necesario: número de rondas, sal y hash. Aunque solo necesitas guardar esta Cadena y no hay necesidad de escribir nada a mano.

  • por supuesto, también puedes usar cualquier otra biblioteca hash fuerte. Pero para la mayoría de ellos, debe implementar la salazón y usar más de 1 rondas usted mismo. Además, no te darán solo una cadena como lo hace bcrypt, aunque tienes que encargarte de almacenar rondas, sal y hash y volver a armarla después.

  • rondas : esta es simplemente la frecuencia con la que debe codificarse la contraseña. Al usar 5000 rondas, la función de hashing devolverá el hash del hash del hash del hash de la contraseña . Básicamente, hay una sola razón para hacer esto: ¡cuesta CPU! Esto significa: cuando alguien trata de aplicar fuerza bruta a su hash, se necesitan 5000 veces más cuando se usan 5000 rondas. Para su aplicación en sí misma no importa demasiado: si el usuario conoce su contraseña, no la reconocerá, si el servidor tomó 0.0004ms o 2ms para validarla.

  • buenas contraseñas : la mejor función de hash es inútil, si la contraseña es demasiado simple. Si se puede descifrar, usando un diccionario, en realidad no importa si has procesado hash con 5000 rondas: tal vez demore unas horas más, pero ¿qué son unas pocas horas, si podrían ser meses o años? Sin embargo, asegúrese de que las contraseñas de sus usuarios contengan las recomendaciones habituales (mayúsculas + minúsculas + números + caracteres especiales, etc.).

9 .: Enviando contraseñas encriptadas por el cable

Si no puede (o no quiere) confiar en HTTPS, pero no desea enviar contraseñas en texto sin formato al iniciar sesión, puede usar la criptografía asimétrica ( http://en.wikipedia.org/wiki/Public-key_cryptography ).

Este servidor crea un par de claves (clave pública y clave privada). ¡La clave pública está disponible para los clientes, la clave privada debe mantenerse en privado!

El cliente ahora puede cifrar datos usando la clave pública, y estos datos solo pueden ser descifrados por el propietario de la clave privada (= el servidor).

Esto no debe (!) Ser utilizado para almacenar contraseñas en la base de datos, ya que si su servidor es pirateado, el pirata informático tendrá las contraseñas encriptadas y la clave privada para el descifrado. Aunque siga usando algoritmos hash (como bcrypt) para almacenar contraseñas en su base de datos. Otra razón es que puede generar fácilmente un nuevo par de claves, si cree que alguien descifró el cifrado.

HTTPS básicamente funciona de la misma manera. Sin embargo, si su aplicación usa HTTPS (que se recomienda) puede que no haya grandes beneficios en términos de seguridad. Pero como se indicó anteriormente, si no puede usar HTTPS por cualquier motivo o no confía en él, esa es una forma de crear su propia conexión segura.

Y tenga en cuenta que una conexión HTTPS real encripta la conexión completa (!) Y todos los datos, no solo los datos de contraseña. Y lo encripta de ambas maneras, de cliente a servidor y de servidor a cliente.





stateless