¿Es seguro token_authenticatable de devise?

79

Estoy construyendo una API simple con la API de Rails y quiero asegurarme de que estoy en el camino correcto aquí. Estoy usando devise para manejar los inicios de sesión y decidí token_authenticatableoptar por la opción de Devise , que genera una clave de API que debe enviar con cada solicitud.

Estoy emparejando la API con una interfaz de backbone / marionette y generalmente me pregunto cómo debo manejar las sesiones. Mi primer pensamiento fue simplemente almacenar la clave de la API en el almacenamiento local o una cookie, y recuperarla al cargar la página, pero algo sobre almacenar la clave de la API de esa manera me molestó desde el punto de vista de la seguridad. ¿No sería fácil obtener la clave de la API, ya sea buscando en el almacenamiento local / la cookie o olfateando cualquier solicitud que se realice, y usarla para hacerse pasar por ese usuario indefinidamente? Actualmente estoy restableciendo la clave de la API en cada inicio de sesión, pero incluso eso parece frecuente: cada vez que inicias sesión en cualquier dispositivo, eso significa que cerrarás la sesión en todos los demás, lo cual es una especie de molestia. Si pudiera eliminar este reinicio, creo que mejoraría desde el punto de vista de la usabilidad.

Puede que esté totalmente equivocado aquí (y espero estarlo), ¿alguien puede explicar si la autenticación de esta manera es confiable y segura, y si no, cuál sería una buena alternativa? En general, estoy buscando una forma de mantener de forma segura a los usuarios "registrados" en el acceso a la API sin forzar la repetición con frecuencia.

Jeff Escalante
fuente

Respuestas:

190

token_authenticatablees vulnerable a los ataques de sincronización, que se explican muy bien en esta publicación de blog . Estos ataques fueron la razón por la que token_authenticatablese eliminó de Devise 3.1. Consulte la publicación del blog de plataformatec para obtener más información.

Para tener el mecanismo de autenticación de token más seguro, el token:

  1. Debe enviarse a través de HTTPS.

  2. Debe ser aleatorio, de fuerza criptográfica.

  3. Debe compararse de forma segura.

  4. No debe almacenarse directamente en la base de datos. Allí solo se puede almacenar un hash del token. (Recuerde, token = contraseña. No almacenamos contraseñas en texto plano en la base de datos, ¿verdad?)

  5. Debería expirar de acuerdo con alguna lógica.

Si renuncia a algunos de estos puntos a favor de la usabilidad, terminará con un mecanismo que no es tan seguro como podría ser. Es tan simple como eso. Sin embargo, debería estar lo suficientemente seguro si cumple los tres primeros requisitos y restringe el acceso a su base de datos.

Ampliando y explicando mi respuesta:

  1. Utilice HTTPS . Este es definitivamente el punto más importante porque se trata de rastreadores.

    Si no usa HTTPS, muchas cosas pueden salir mal. Por ejemplo:

    • Para transmitir de forma segura las credenciales del usuario (nombre de usuario / correo electrónico / contraseña), tendría que usar la autenticación implícita, pero eso no es suficiente en estos días, ya que los hashes salados pueden ser forzados .

    • En Rails 3, las cookies solo están protegidas por la codificación Base64, por lo que pueden revelarse con bastante facilidad. Consulte Decodificación de cookies de sesión de Rails para obtener más información.

      Sin embargo, desde Rails 4, el almacén de cookies está encriptado, por lo que los datos se verifican digitalmente y son ilegibles para un atacante. Las cookies deben ser seguras siempre que secret_key_baseno se filtren.

  2. Genera tu token con:

    Para obtener una explicación de por qué esto es necesario, sugiero leer el sysrandomarchivo README y la publicación del blog Cómo generar números aleatorios seguros en varios lenguajes de programación .

  3. Busque el registro de usuario utilizando la identificación del usuario, el correo electrónico o algún otro atributo. Luego, compare el token de ese usuario con el token de la solicitud con Devise.secure_compare(user.auth_token, params[:auth_token]. Si está en Rails 4.2.1+ también puede usar ActiveSupport::SecurityUtils.secure_compare.

    No , no encontrar el registro de usuario con un buscador de los rieles como User.find_by(auth_token: params[:auth_token]). ¡Esto es vulnerable a los ataques de tiempo!

  4. Si va a tener varias aplicaciones / sesiones al mismo tiempo por usuario, entonces tiene dos opciones:

    • Almacene el token no cifrado en la base de datos para que se pueda compartir entre dispositivos. Esta es una mala práctica, pero supongo que puede hacerlo en nombre de UX (y si confía en sus empleados con acceso a la base de datos).

    • Almacene tantos tokens cifrados por usuario como desee para permitir las sesiones actuales. Entonces, si desea permitir 2 sesiones en 2 dispositivos diferentes, mantenga 2 hash de token distintos en la base de datos. Esta opción es un poco menos sencilla de implementar, pero definitivamente es más segura. También tiene la ventaja de permitirle brindar a sus usuarios la opción de finalizar las sesiones activas actuales en dispositivos específicos revocando sus tokens (al igual que lo hacen GitHub y Facebook).

  5. Debería haber algún tipo de mecanismo que haga que el token caduque. Al implementar este mecanismo, tenga en cuenta el compromiso entre UX y seguridad.

    Google expira un token si no se ha utilizado durante seis meses .

    Facebook expira un token si no se ha utilizado durante dos meses :

    Las aplicaciones móviles nativas que utilizan los SDK de Facebook obtendrán tokens de acceso de larga duración, válidos durante unos 60 días. Estos tokens se actualizarán una vez al día cuando la persona que usa su aplicación realice una solicitud a los servidores de Facebook. Si no se realizan solicitudes, el token caducará después de aproximadamente 60 días y la persona tendrá que realizar el proceso de inicio de sesión nuevamente para obtener un nuevo token.

  6. Actualice a Rails 4 para usar su almacén de cookies cifradas. Si no puede, cifre el almacén de cookies usted mismo, como se sugiere aquí . No habría ningún problema en almacenar un token de autenticación en un almacén de cookies cifradas.

También debe tener un plan de contingencia, por ejemplo, una tarea de rake para restablecer un subconjunto de tokens o cada token en la base de datos.

Para comenzar, puede consultar esta esencia (por uno de los autores de Devise) sobre cómo implementar la autenticación de token con Devise. Finalmente, el Railscast sobre cómo asegurar una API debería ser útil.

Ashitaka
fuente
Impresionante, esto ayuda mucho, ¡gracias! Esto casi definitivamente obtendrá la respuesta correcta. La recompensa es suya si agrega a esto su opinión / recomendación (específicamente) sobre la mejor manera de manejar la autenticación de API :)
Jeff Escalante
1
Si bien ambos métodos generan una cadena aleatoria, urlsafe_base64genera una cadena segura para URL. Todo está en el nombre. A menos que desee usar su token en su URL (lo cual no debería ), use hex.
Ashitaka
2
token! = contraseña. No hay nada de malo en almacenar un token en texto sin formato. El problema con el almacenamiento de contraseñas en texto sin formato es que la contraseña se puede usar en otro lugar, ese no debería ser el caso con su token.
fatfrog
2
@fatfrog No. Token == contraseña. Si un pirata informático o un empleado descontento tiene acceso a su base de datos, no debería poder autenticarse como un determinado usuario o administrador.
Ashitaka
1
No estoy de acuerdo, si un pirata informático o un empleado descontento tiene acceso a su base de datos, los tokens son lo último de lo que debe preocuparse. Ellos ya tienen tus datos.
fatfrog
3

Puede intentar usar rails4 con su API, está brindando más seguridad y usa devise 3.1.0rc

Para el token, tienda de sesión, puede ir a http://ruby.railstutorial.org/chapters/sign-in-sign-out y http://blog.bigbinary.com/2013/03/19/cookies-on-rails .html para más comprensible.

Por último, debería pasar por este tipo de cifrado y descifrado " No se puede descifrar los datos cifrados almacenados " para obtener más seguridad.

Gaurav Sharma
fuente
¿Alguien tiene una alternativa de ejemplo personalizada?
Daniel Morris