Je construis une API simple avec API Rails , et je veux m'assurer que je suis sur la bonne voie ici. J'utilise devise pour gérer les connexions et j'ai décidé d'aller avec Devise's token_authenticatable
option, qui génère une clé API que vous devez envoyer avec chaque demande.
J'associe l'API avec un frontal de backbone/marionette et je me demande généralement comment gérer les sessions. Ma première pensée a été de simplement stocker la clé API dans le stockage local ou un cookie et de la récupérer au chargement de la page, mais quelque chose à propos du stockage de la clé API de cette façon m'a dérangé du point de vue de la sécurité. Ne serait-il pas facile de saisir la clé API soit en regardant dans le stockage local/le cookie ou en reniflant toute demande qui passe, et en l'utilisant pour usurper l'identité de cet utilisateur indéfiniment? Je réinitialise actuellement la clé API à chaque connexion, mais même cela semble fréquent - chaque fois que vous vous connectez sur n'importe quel appareil, cela signifie que vous serez déconnecté sur tous les autres, ce qui est un peu pénible. Si je pouvais abandonner cette réinitialisation, je pense que cela s'améliorerait du point de vue de l'utilisabilité.
Je peux me tromper complètement ici (et j'espère que je le suis), quelqu'un peut-il expliquer si l'authentification de cette manière est fiable et sinon, quelle serait une bonne alternative? Dans l'ensemble, je cherche un moyen de garder en toute sécurité les utilisateurs "connectés" à l'accès à l'API sans forcer fréquemment la nouvelle authentification.
token_authenticatable
Est vulnérable aux attaques de synchronisation, qui sont très bien expliquées dans ce billet de blog . Ces attaques sont la raison pour laquelle token_authenticatable
A été supprimé de Devise 3.1. Voir le article de blog plataformatec pour plus d'informations.
Pour disposer du mécanisme d'authentification par jeton le plus sécurisé, le jeton:
Doit être envoyé via HTTPS.
Doit être aléatoire, d'une puissance cryptographique.
Doit être comparé en toute sécurité.
Ne doit pas être stocké directement dans la base de données. Seul un hachage du jeton peut y être stocké. (N'oubliez pas, token = mot de passe. Nous ne stockons pas les mots de passe en texte brut dans la base de données, non?)
Devrait expirer selon une logique.
Si vous renoncez à certains de ces points en faveur de la convivialité, vous vous retrouverez avec un mécanisme qui n'est pas aussi sûr qu'il pourrait l'être. C'est aussi simple que ça. Vous devez cependant être suffisamment en sécurité si vous remplissez les trois premières conditions et limitez l'accès à votre base de données.
Élargir et expliquer ma réponse:
Utilisez HTTPS . C'est certainement le point le plus important car il traite des renifleurs.
Si vous n'utilisez pas HTTPS, beaucoup de choses peuvent mal se passer. Par exemple:
Pour transmettre en toute sécurité les informations d'identification de l'utilisateur (nom d'utilisateur/e-mail/mot de passe), vous devez utiliser l'authentification Digest, mais cela ne suffit plus de nos jours car les hachages salés peuvent être forcés brutalement .
Dans Rails 3, les cookies ne sont enveloppés que par l'encodage Base64, donc ils peuvent être assez facilement révélés. Voir Décodage Rails Cookies de session pour plus d'informations.
Depuis Rails 4 cependant, le magasin de cookies est crypté de sorte que les données sont à la fois vérifiées numériquement et illisibles pour un attaquant. Les cookies doivent être sécurisés tant que votre secret_key_base
N'est pas divulgué.
Générez votre token avec:
SecureRandom.hex
uniquement si vous êtes sur Ruby 2.5+.sysrandom
si vous êtes sur un Ruby plus ancien.Pour une explication sur la raison pour laquelle cela est nécessaire, je suggère de lire les sysrandom
README et l'article de blog Comment générer Secure Nombres aléatoires dans divers langages de programmation .
Recherchez l'enregistrement utilisateur à l'aide de l'ID utilisateur, de l'e-mail ou d'un autre attribut. Ensuite, comparez le jeton de cet utilisateur avec le jeton de la demande avec Devise.secure_compare(user.auth_token, params[:auth_token]
. Si vous êtes sur Rails 4.2.1+, vous pouvez également utiliser ActiveSupport::SecurityUtils.secure_compare
.
Ne pas trouver l'enregistrement utilisateur avec un Rails Finder comme User.find_by(auth_token: params[:auth_token])
. Ceci est vulnérable aux synchronisation des attaques!
Si vous allez avoir plusieurs applications/sessions en même temps par utilisateur, vous avez deux options:
Stockez le jeton non chiffré dans la base de données afin qu'il puisse être partagé entre les appareils. C'est une mauvaise pratique, mais je suppose que vous pouvez le faire au nom de l'UX (et si vous faites confiance à vos employés avec un accès DB).
Stockez autant de jetons chiffrés par utilisateur que vous le souhaitez pour autoriser les sessions en cours. Donc, si vous souhaitez autoriser 2 sessions sur 2 appareils différents, conservez 2 hachages de jetons distincts dans la base de données. Cette option est un peu moins simple à mettre en œuvre mais elle est certainement plus sûre. Il a également l'avantage de vous permettre de donner à vos utilisateurs la possibilité de mettre fin aux sessions actives en cours sur des appareils spécifiques en révoquant leurs jetons (tout comme GitHub et Facebook le font).
Il devrait y avoir une sorte de mécanisme qui provoque l'expiration du jeton. Lors de la mise en œuvre de ce mécanisme, prenez en compte le compromis entre UX et sécurité.
Google expire un jeton s'il n'a pas été utilisé pendant six mois .
Facebook expire un jeton s'il n'a pas été utilisé pendant deux mois :
Les applications mobiles natives utilisant les SDK de Facebook obtiendront des jetons d'accès longue durée, valables pendant environ 60 jours. Ces jetons seront actualisés une fois par jour lorsque la personne utilisant votre application fera une demande aux serveurs de Facebook. Si aucune demande n'est faite, le jeton expirera après environ 60 jours et la personne devra à nouveau parcourir le flux de connexion pour obtenir un nouveau jeton.
Mettre à niveau vers Rails 4 pour utiliser son magasin de cookies crypté. Si vous ne le pouvez pas, alors cryptez le magasin de cookies vous-même, comme suggéré ici . Il n'y aurait absolument aucun problème en stockant un jeton d'authentification dans un magasin de cookies crypté.
Vous devez également avoir un plan d'urgence, par exemple, une tâche de râteau pour réinitialiser un sous-ensemble de jetons ou chaque jeton unique dans la base de données.
Pour commencer, vous pouvez consulter ce Gist (par l'un des auteurs de Devise) sur la façon d'implémenter l'authentification par jeton avec Devise. Enfin, le Railscast sur la sécurisation d'une API devrait être utile.
Selon le fichier README du projet, la gemme devise_token_auth a été inspirée par ce post StackOverflow: https://github.com/lynndylanhurley/devise_token_auth
Vous pouvez essayer d'utiliser Rails4 avec votre API, il offre plus de sécurité et utilise devise 3.1.0rc
Dans Rails 4.0, plusieurs fonctionnalités ont été extraites en gemmes.
Devise 3.1.0.rc fonctionne sur les deux Rails 3.2 et Rails 4.0. http://blog.plataformatec.com.br/2013)/08/devise-3-1-now-with-more-secure-defaults /
Devise est la dépréciation de TokenAuthenticatable
dans 3.1.0rc mais vous pouvez créer votre propre méthode TokenAuthenticatable
pour un problème de sécurité. C'est plus fiable et sécurisé.
Pour les jetons, le magasin de sessions, vous pouvez passer par http://Ruby.railstutorial.org/chapters/sign-in-sign-out et http://blog.bigbinary.com/ 2013/03/19/cookies-on-Rails.html pour plus de clarté.
Enfin, vous devriez passer par ce type de cryptage et de décryptage " Impossible de décrypter les données cryptées stockées " pour obtenir plus de sécurité.