Les jetons Web JSON sont faciles à utiliser et à déboguer, mais ils offrent également une amélioration impressionnante de la sécurité.

L'authentification brisée continue d'être une vulnérabilité persistante dans les applications Web modernes - elle se classe toujours parmi les 10 principaux risques de sécurité des API de l'OWASP.

Les effets de cette vulnérabilité peuvent être graves. Ils peuvent accorder un accès non autorisé à des données sensibles et compromettre l'intégrité du système. Pour garantir efficacement un accès sécurisé aux applications et à leurs ressources, il est essentiel que vous utilisiez des mécanismes d'authentification robustes.

Découvrez comment vous pouvez implémenter l'authentification des utilisateurs dans Flask à l'aide de JSON Web Tokens (JWT), une méthode populaire et efficace basée sur des jetons.

Authentification basée sur des jetons à l'aide de jetons Web JSON

L'authentification basée sur les jetons utilise une chaîne de caractères cryptée pour valider et autoriser l'accès à un système ou à une ressource. Vous pouvez implémenter ce type d'authentification à l'aide de différentes méthodes, notamment les jetons de session, les clés API et les jetons Web JSON.

instagram viewer

Les JWT, en particulier, offrent une approche sécurisée et compacte pour la transmission des informations d'identification des utilisateurs requises entre les applications côté client et les serveurs.

Un JWT se compose de trois composants principaux: l'en-tête, la charge utile et la signature. L'en-tête contient des métadonnées sur le jeton, y compris l'algorithme de hachage utilisé pour coder le jeton.

La charge utile contient les informations d'identification réelles de l'utilisateur, telles que l'ID utilisateur et les autorisations. Enfin, la signature assure la validité du jeton en vérifiant son contenu à l'aide d'une clé secrète.

À l'aide de JWT, vous pouvez authentifier les utilisateurs et stocker les données de session dans le jeton lui-même.

Configurer un projet Flask et une base de données MongoDB

Pour commencer, créez un nouveau répertoire de projet à l'aide d'un terminal :

projet de flacon mkdir
cd flacon-projet

Ensuite, installez environnement virtuel, pour créer un environnement de développement virtuel local pour votre projet Flask.

virtualenv venv

Enfin, activez l'environnement virtuel.

# Unix ou MacOS: 
source venv/bin/activer

# Les fenêtres:
.\venv\Scripts\activate

Vous pouvez trouver le code de ce projet dans ce Référentiel GitHub.

Installer les packages requis

Dans le répertoire racine de votre dossier de projet, créez un nouveau exigences.txt fichier et ajoutez ces dépendances pour le projet :

ballon
pyjwt
python-dotenv
pymongo
bcrypt

Enfin, exécutez la commande ci-dessous pour installer les packages. Assurez-vous que vous avez pépin (gestionnaire de packages) installé; sinon, installez-le sur votre système Windows, Mac ou Linux.

pip install -r exigences.txt

Créer une base de données MongoDB

Allez-y et créez une base de données MongoDB. Tu peux configurer une base de données MongoDB locale, alternativement, créer un cluster sur MongoDB Atlas, un service MongoDB basé sur le cloud.

Une fois que vous avez créé la base de données, copiez l'URI de connexion, créez un .env fichier dans le répertoire racine de votre projet, et ajoutez-le comme suit :

MONGO_URI=""

Enfin, configurez la connexion à la base de données depuis votre application Flask. Créer un nouveau utils/db.py fichier dans le répertoire racine de votre projet, avec ce code :

depuis pymongo importer MongoClient

définitivementconnect_to_mongodb(mongo_uri):
client = MongoClient (mongo_uri)
db = client.get_database("utilisateurs")
retour db

Cette fonction établit une connexion à la base de données MongoDB à l'aide de l'URI de connexion fourni. Il crée alors un nouveau utilisateurs collection si elle n'existe pas, et renvoie l'instance de base de données correspondante.

Créer le serveur Web Flask

Avec la base de données configurée, continuez et créez un app.py dans le répertoire racine du dossier du projet et ajoutez le code suivant pour créer une instance de l'application Flask.

depuis ballon importer Ballon
depuis routes.user_auth importer registre_routes
depuis utils.db importer connect_to_mongodb
importer os
depuis dotenv importer load_dotenv

app = flacon (__nom__)
load_dotenv()

mongo_uri = os.getenv('MONGO_URI')
db = connect_to_mongodb (mongo_uri)

register_routes (application, base de données)

si __nom__ == '__principal__':
app.run (débogage=Vrai)

Créer les points de terminaison de l'API d'authentification

Pour implémenter l'authentification des utilisateurs dans votre application Flask, il est essentiel de définir les points de terminaison d'API nécessaires qui gèrent les opérations liées à l'authentification.

Cependant, commencez par définir le modèle pour les données des utilisateurs. Pour ce faire, créez un nouveau modèle/user_model.py fichier dans le répertoire racine et ajoutez le code suivant.

depuis pymongo.collection importer Collection
depuis bson.objectid importer ID d'objet

classeUtilisateur:
définitivement__init__(self, collection: Collection, nom d'utilisateur: str, mot de passe: str):
self.collection = collection
self.username = nom d'utilisateur
self.password = mot de passe
définitivementsauvegarder(soi):
données_utilisateur = {
'nom d'utilisateur': self.username,
'mot de passe': self.password
}
result = self.collection.insert_one (user_data)
retour str (result.insert_id)

@méthodestatique
définitivementfind_by_id(collection: Collection, user_id: str):
retour collection.find_one({'_identifiant': ObjectId (identifiant_utilisateur)})

@méthodestatique
définitivementfind_by_username(collection: Collection, nom d'utilisateur: str):
retour collection.find_one({'nom d'utilisateur': nom d'utilisateur})

Le code ci-dessus spécifie un Utilisateur classe qui sert de modèle de données et définit plusieurs méthodes pour interagir avec une collection MongoDB afin d'effectuer des opérations liées à l'utilisateur.

  1. Le sauvegarder La méthode enregistre un nouveau document utilisateur avec le nom d'utilisateur et le mot de passe fournis dans la collection MongoDB et renvoie l'ID du document inséré.
  2. Le find_by_id et find_by_username Les méthodes récupèrent les documents utilisateur de la collection en fonction de l'ID utilisateur ou du nom d'utilisateur fourni, respectivement.

Définir les routes d'authentification

  1. Commençons par définir la route d'enregistrement. Cette route ajoutera de nouvelles données utilisateur à la collection d'utilisateurs MongoDB. Dans le répertoire racine, créez un nouveau routes/user_auth.py fichier et le code suivant.
    importer jwt
    depuis outils de fonction importer enveloppe
    depuis ballon importer jsonify, demande, make_response
    depuis modèles.user_model importer Utilisateur
    importer bcrypt
    importer os

    définitivementregistre_routes(application, base de données):
    collection = db.users
    app.config['CLEF SECRÈTE'] = os.urandom(24)

    @app.route('/api/register', methodes=['POST'])
    définitivementenregistrer():

    nom d'utilisateur = request.json.get('nom d'utilisateur')
    mot de passe = request.json.get('mot de passe')

    existing_user = User.find_by_username (collection, nom d'utilisateur)
    si utilisateur existant:
    retour jsonifier({'message': 'Ce nom d'utilisateur existe déjà!'})

    hashed_password = bcrypt.hashpw (password.encode('utf-8'), bcrypt.gensalt())
    new_user = Utilisateur (collection, nom d'utilisateur, hashed_password.decode('utf-8'))
    user_id = new_user.save()

    retour jsonifier({'message': 'L'utilisateur s'est enregistré avec succès !', 'ID de l'utilisateur': ID de l'utilisateur})

  2. Implémentez la fonctionnalité de connexion pour gérer le processus d'authentification et vérifier les informations d'identification de l'utilisateur. Sous la route d'enregistrement, ajoutez le code suivant.
     @app.route('/api/login', methodes=['POST'])
    définitivementconnexion():
    nom d'utilisateur = request.json.get('nom d'utilisateur')
    mot de passe = request.json.get('mot de passe')
    user = User.find_by_username (collection, nom d'utilisateur)
    si utilisateur:
    si bcrypt.checkpw (mot de passe.encode('utf-8'), utilisateur['mot de passe'].encoder('utf-8')):
    jeton = jwt.encode({'ID de l'utilisateur': chaîne (utilisateur['_identifiant'])}, app.config['CLEF SECRÈTE'], algorithme='HS256')

    réponse = make_response (jsonify({'message': 'Connexion réussie!'}))
    réponse.set_cookie('jeton', jeton)
    retour réponse

    retour jsonifier({'message': 'Nom d'utilisateur ou mot de passe invalide'})

    Le point de terminaison de connexion fait deux choses: il vérifie les informations d'identification de l'utilisateur fournies et, une fois l'authentification réussie, il génère un JWT unique pour cet utilisateur. Il définit ce jeton en tant que cookie dans la réponse, ainsi qu'une charge utile JSON indiquant une connexion réussie. Si les informations d'identification ne sont pas valides, il renverra une réponse JSON pour l'indiquer.
  3. Définissez une fonction de décoration qui vérifie les jetons Web JSON (JWT) transmis avec les demandes d'API ultérieures. Ajoutez le code ci-dessous dans le registre_routes bloc de code de fonction.
    définitivementtoken_required(F):
    @wraps (f)
    définitivementdécoré(*args, **kwargs):
    jeton = request.cookies.get('jeton')

    sipas jeton:
    retour jsonifier({'message': 'Jeton manquant !'}), 401

    essayer:
    data = jwt.decode (jeton, app.config['CLEF SECRÈTE'], algorithmes=['HS256'])
    current_user = User.find_by_id (collection, données['ID de l'utilisateur'])
    sauf jwt. Erreur de signature expirée :
    retour jsonifier({'message': 'Le jeton a expiré !'}), 401
    sauf jwt. Erreur de jeton invalide :
    retour jsonifier({'message': 'Jeton invalide!'}), 401

    retour f (utilisateur_actuel, *args, **kwargs)

    retour décoré

    Cette fonction de décorateur garantit la présence d'un jeton JWT valide dans les requêtes API ultérieures. Il vérifie si le jeton est manquant, expiré ou valide, et renvoie une réponse JSON appropriée si c'est le cas.
  4. Enfin, créez un itinéraire protégé.
     @app.route('/api/users', methodes=['GET'])
    @token_required
    définitivementget_users(utilisateur actuel):
    utilisateurs = liste (collection.find({}, {'_identifiant': 0}))
    retour jsonify (utilisateurs)

Ce point de terminaison gère la logique de récupération des données utilisateur à partir de la base de données, mais il nécessite que le client qui envoie les demandes inclue un jeton valide pour accéder aux données.

Enfin, exécutez la commande ci-dessous pour lancer le serveur de développement.

course de ballon

Pour tester l'enregistrement, la connexion et le point de terminaison des utilisateurs protégés, vous pouvez utiliser Postman ou tout autre client API. Envoyez vos demandes à http://localhost: 5000/api/et observez les réponses pour vérifier la fonctionnalité de ces points de terminaison d'API.

L'authentification par jeton est-elle une mesure de sécurité infaillible?

Les jetons Web JSON offrent un moyen robuste et efficace d'authentifier les utilisateurs pour votre application Web. Cependant, il est important de comprendre que l'authentification par jeton n'est pas infaillible; ce n'est qu'une pièce d'un puzzle de sécurité plus vaste.

Combinez l'authentification par jeton avec d'autres bonnes pratiques de sécurité. N'oubliez pas de surveiller en permanence et d'adopter des pratiques de sécurité cohérentes; vous améliorerez considérablement la sécurité globale de vos applications Flask.