• Assurez la sécurité des applications de la bonne manière! Détectez, protégez, surveillez, accélérez et plus encore…
  • Apprenons à sécuriser une API REST avec des jetons Web JSON pour empêcher les utilisateurs et les applications tierces d'en abuser.

    Nous allons construire un service de base de données en utilisant SQLite et permettre aux utilisateurs d'y accéder via une API REST à l'aide de méthodes HTTP telles que POST et PUT.

    De plus, nous découvrirons pourquoi les jetons Web JSON sont un moyen approprié de protéger l'API de repos au lieu de l'authentification Digest et de base. Avant de continuer, comprenons le terme jetons Web JSON, API REST et framework Flask.

    Jetons Web JSON

    Jeton Web JSON, également appelé JWT, est le moyen sécurisé de transférer des jetons aléatoires entre deux parties ou entités. JSON est généralement composé de trois parties comme suit.

    • Charge utile
    • En-tête
    • Signature

    JSON utilise deux types de formulaires de structure lors du transfert de données ou d'informations entre deux parties.

    • Sérialisé
    • Désérialisé

    Le formulaire sérialisé est utilisé lors du transfert de données vers le réseau via chaque demande et réponse, tandis que le formulaire désérialisé est utilisé lors de la lecture et de l'écriture de données sur le jeton Web.

    Dans la forme sérialisée, il y a trois composants.

    • En-tête
    • Charge utile
    • Signature

    Le composant d'en-tête définit les informations cryptographiques sur les jetons. Par exemple:

    • Est-ce un JWT signé ou non signé?
    • Définir les techniques d'algorithme

    Le formulaire désérialisé, contrairement au formulaire sérialisé, contient deux composants.

    • Charge utile
    • En-tête

    API REST

    L'API (interface de programmation d'application) permet la communication entre deux applications pour récupérer ou soumettre les données. Il existe deux types d'API courants: les API Web et système.

    Dans cet article, nous examinerons uniquement l'API Web. Il existe deux types d'API Web.

    • Request - API de réponse: repos, GraphQL, appel de procédure à distance (RPC)
    • API événementielle: WebHooks, Web Sockets, streaming HTTP

    L'API REST appartient à la catégorie requête-réponse. Il utilise des méthodes HTTP telles que GET, POST et PUT pour effectuer des opérations API.

    Un exemple classique est lorsqu'un utilisateur envoie une méthode GET au service Web pour demander ou récupérer une ressource spécifique ou une collection de ressources. Le serveur renvoie ensuite la ressource spécifique ou la collection de ressources à l'utilisateur qui l'a demandée.

    Cadre de flacon

    Flask est un framework basé sur python. Il s'agit d'un micro-framework utilisé par les développeurs python pour créer une API de repos. On l'appelle un micro-framework car il permet aux développeurs, par exemple, d'ajouter une authentification personnalisée et tout autre système backend basé sur les préférences.

    Commençons par l'implémentation. La configuration de mon système est la suivante.

    • Ubuntu comme système d'exploitation
    • Python 2.7 +
    • Facteur

    Configurer un environnement virtuel à l'aide de virtualenv

    Nous devons mettre en place un environnement virtuel pour garantir que certains packages n'entreront pas en conflit avec les packages système. Utilisons le virtualenv pour mettre en place un nouvel environnement virtuel.

    En supposant que vous ayez le pip commande disponible sur votre système, exécutez la commande suivante via pip à installer.

    pip install virtualenv

    Si vous n'avez pas de pip sur votre machine, suivez ceci Documentation pour installer pip sur votre système.

    Ensuite, créons un répertoire pour stocker ou contenir notre environnement virtuel. Utilisez le mkdir commande ci-dessous pour créer un répertoire

    mkdir flaskproject

    Changer en flaskproject répertoire à l'aide de la commande suivante

    cd flaskproject

    À l'intérieur de l' flaskproject répertoire, utilisez le virtualenv outil pour créer un environnement virtuel comme indiqué ci-dessous:

    virtualenv flaskapi

    Après avoir utilisé le virtualenv outil pour créer l'environnement virtuel, exécutez le cd commande pour changer en flaskapi répertoire comme environnement virtuel et activez-le à l'aide de la commande ci-dessous.

    source bin/activate

    Exécutez toutes les tâches liées à ce projet dans l'environnement virtuel.

    Installer des packages à l'aide de pip

    Il est maintenant temps d'installer des packages tels que le framework flask et PyJWT que nous utiliserons pour créer le reste de l'API et d'autres packages nécessaires pour notre projet d'API.

    Créer un requirements.txt fichier avec les packages suivants.

    Flask  
    datetime 
    uuid
    Flask-SQLAlchemy
    PyJWT

    Installez-les avec pip.

    pip install -r requirements.txt

    Configurer une base de données

    Installons SQLite.

    apt-get install sqlite3

    Créez une base de données nommée la bibliothèque. Dans cette base de données, nous allons créer deux tables, à savoir le Users et Authors tableau.

    La table des utilisateurs contiendra les utilisateurs enregistrés. Seuls les utilisateurs enregistrés peuvent avoir accès à la table Auteurs.

    La table des auteurs stockera les informations sur les auteurs ou les détails tels que le nom de l'auteur, le pays de naissance, etc. soumis par les utilisateurs enregistrés.

    Créez la base de données à l'aide de la commande suivante:

    sqlite3 library.db

    Vous pouvez vérifier si vous avez créé la base de données avec succès en utilisant la commande ci-dessous:

    .databases

    Ouvrez un nouveau terminal et exécutez ce qui suit dans l'environnement virtuel que nous avons créé précédemment.

    touch app.py

    Collez le code suivant dans le fichier nommé app.py

    from flask import Flask, request, jsonify, make_response
    from flask_sqlalchemy import SQLAlchemy
    from werkzeug.security import generate_password_hash, check_password_hash
    import uuid
    import jwt
    import datetime
    from functools import wraps

    La première ligne du code ci-dessus importe des packages tels que request et jsonify. Nous utiliserons request pour garder une trace des données au niveau de la demande lors d'une demande et d'une utilisation jsonify pour générer des réponses dans un JSON le format.

    Sur la ligne suivante, nous avons importé SQLAlchemy de flask_sqlalchemy afin d'intégrer les fonctionnalités de SQLAlchemy dans le flask.

    De werkzeug.security, nous avons importé generate_password_hash pour générer un hachage de mot de passe pour les utilisateurs et check_password_hash pour vérifier le mot de passe de l'utilisateur lors de la comparaison du mot de passe soumis par les utilisateurs avec les mots de passe des utilisateurs stockés dans la base de données.

    Enfin, nous avons importé uuid également connu sous le nom d'identificateurs uniques universels pour générer des numéros d'identification aléatoires pour les utilisateurs.

    Pourtant, à l'intérieur du app.py , implémentez les paramètres de configuration de l'API de la bibliothèque à l'aide du code ci-dessous dans le fichier app.py.

    Placez le code suivant sous l'instruction d'importation.

    app = Flask(__name__)
    
    app.config['SECRET_KEY']='Th1s1ss3cr3t'
    app.config['SQLALCHEMY_DATABASE_URI']='sqlite://///home/michael/geekdemos/geekapp/library.db'
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
    
    db = SQLAlchemy(app)

    Créez maintenant deux modèles pour le tableau Utilisateurs et auteurs comme indiqué ci-dessous. Copiez et collez le code dans le fichier app.py.

    Placez le code ci-dessous juste en dessous de ce paramètre de base de données  db = SQLAlchemy(app)

    class Users(db.Model):
         id = db.Column(db.Integer, primary_key=True)
         public_id = db.Column(db.Integer)
         name = db.Column(db.String(50))
         password = db.Column(db.String(50))
         admin = db.Column(db.Boolean)
    class Authors(db.Model):
         id = db.Column(db.Integer, primary_key=True)
         name = db.Column(db.String(50), unique=True, nullable=False))
         book = db.Column(db.String(20), unique=True, nullable=False))
         country = db.Column(db.String(50), nullable=False))
         booker_prize = db.Column(db.Boolean)

    Générer des tableaux d'utilisateurs et d'auteurs

    Sur le terminal, tapez le code suivant dans l'environnement virtuel pour générer ou créer des tables pour les tables Utilisateurs et Auteurs comme indiqué ci-dessous

    from app import db
    db.create_all()

    Ensuite, ouvrez le app.py fichier dans l'environnement virtuel et créez une autre fonction.

    Cette fonction générera des jetons afin de permettre uniquement aux utilisateurs enregistrés d'accéder et d'exécuter un ensemble d'opérations API sur la table Authors.

    Placez ce code sous le modèle de base de données pour la table Authors

    def token_required(f):
       @wraps(f)
       def decorator(*args, **kwargs):
    
          token = None
    
          if 'x-access-tokens' in request.headers:
             token = request.headers['x-access-tokens']
    
          if not token:
             return jsonify({'message': 'a valid token is missing'})
    
          try:
             data = jwt.decode(token, app.config[SECRET_KEY])
             current_user = Users.query.filter_by(public_id=data['public_id']).first()
          except:
             return jsonify({'message': 'token is invalid'})
    
            return f(current_user, *args, **kwargs)
       return decorator

    Créer des itinéraires pour la table des utilisateurs

    Créons maintenant une route pour permettre aux utilisateurs de s'inscrire à l'API Authors via un nom d'utilisateur et un mot de passe, comme indiqué ci-dessous.

    Ouvrez à nouveau le app.py fichier dans l'environnement virtuel et collez le code suivant sous la fonction token_required(f)

    @app.route('/register', methods=['GET', 'POST'])
    def signup_user():  
     data = request.get_json()  
    
     hashed_password = generate_password_hash(data['password'], method='sha256')
     
     new_user = Users(public_id=str(uuid.uuid4()), name=data['name'], password=hashed_password, admin=False) 
     db.session.add(new_user)  
     db.session.commit()    
    
     return jsonify({'message': 'registered successfully'})

    Dans l'environnement virtuel, créez un autre itinéraire dans le app.py fichier pour permettre aux utilisateurs enregistrés de se connecter.

    Lorsqu'un utilisateur se connecte, un jeton aléatoire est généré pour que l'utilisateur puisse accéder à l'API de la bibliothèque.

    Collez le code ci-dessous sous l'itinéraire précédent que nous avons créé.

    @app.route('/login', methods=['GET', 'POST'])  
    def login_user(): 
     
      auth = request.authorization   
    
      if not auth or not auth.username or not auth.password:  
         return make_response('could not verify', 401, {'WWW.Authentication': 'Basic realm: "login required"'})    
    
      user = Users.query.filter_by(name=auth.username).first()   
         
      if check_password_hash(user.password, auth.password):  
         token = jwt.encode({'public_id': user.public_id, 'exp' : datetime.datetime.utcnow() + datetime.timedelta(minutes=30)}, app.config['SECRET_KEY'])  
         return jsonify({'token' : token.decode('UTF-8')}) 
    
      return make_response('could not verify',  401, {'WWW.Authentication': 'Basic realm: "login required"'})

    Pourtant, dans l'environnement virtuel, créez une autre route dans le app.py fichier pour obtenir ou récupérer tous les utilisateurs enregistrés.

    Ce code vérifie tous les utilisateurs enregistrés dans la table Users et renvoie le résultat final au format JSON.

    Collez le code ci-dessous sous la route de connexion

    @app.route('/users', methods=['GET'])
    def get_all_users():  
       
       users = Users.query.all() 
    
       result = []   
    
       for user in users:   
           user_data = {}   
           user_data['public_id'] = user.public_id  
           user_data['name'] = user.name 
           user_data['password'] = user.password
           user_data['admin'] = user.admin 
           
           result.append(user_data)   
    
       return jsonify({'users': result})

    Créer des itinéraires pour la table des auteurs 

    Créons des routes pour la table Authors afin de permettre aux utilisateurs de récupérer tous les auteurs de la base de données, ainsi que de supprimer les auteurs.

    Seuls les utilisateurs disposant de jetons valides peuvent effectuer ces opérations d'API.

    Dans le fichier app.py, créez une route permettant aux utilisateurs enregistrés de créer de nouveaux auteurs.

    Collez ce code sous l'itinéraire qui permet à un utilisateur de récupérer tous les utilisateurs enregistrés.

    @app.route('/author', methods=['POST', 'GET'])
    @token_required
    def create_author(current_user):
       
       data = request.get_json() 
    
       new_authors = Authors(name=data['name'], country=data['country'], book=data['book'], booker_prize=True, user_id=current_user.id)  
       db.session.add(new_authors)   
       db.session.commit()   
    
       return jsonify({'message' : 'new author created'})

    Ensuite, créez une autre route pour permettre à un utilisateur enregistré avec un jeton valide de récupérer tous les auteurs dans le tableau Auteurs comme indiqué ci-dessous.

    Collez ce code sous l'itinéraire qui permet à un utilisateur de créer un nouvel auteur.

    @app.route('/authors', methods=['POST', 'GET'])
    @token_required
    def get_authors(current_user):
    
        authors = Authors.query.filter_by(user_id=current_user.id).all()
    
        output = []
        for author in authors:
    
               author_data = {}
               author_data['name'] = author.name
               author_data['book'] = author.book
               author_data['country'] = author.country
               author_data['booker_prize'] = author.booker_prize
               output.append(author_data)
    
         return jsonify({'list_of_authors' : output})

    Enfin, toujours à l'intérieur du app.py fichier, créez une route pour supprimer un auteur spécifié comme indiqué ci-dessous.

    Collez ce code sous l'itinéraire qui permet à un utilisateur de récupérer une liste d'auteurs.

    @app.route('/authors/<author_id>', methods=['DELETE'])
    @token_required
    def delete_author(current_user, author_id):  
        author = Author.query.filter_by(id=author_id, user_id=current_user.id).first()   
        if not author:   
           return jsonify({'message': 'author does not exist'})   
    
    
        db.session.delete(author)  
        db.session.commit()   
    
        return jsonify({'message': 'Author deleted'})
    
    
    if  __name__ == '__main__':  
         app.run(debug=True)

    Ensuite, enregistrez et fermez le fichier app.py dans l'environnement virtuel.

    Test de l'API de la bibliothèque avec Postman

    Dans cette section, nous utiliserons un outil de facteur pour envoyer une demande aux services de base de données. Si vous n'avez pas de facteur sur votre machine, vous pouvez découvrir comment le télécharger et l'installer ici.

    Outre le facteur, nous pouvons utiliser d'autres outils tels que Friser pour envoyer des requêtes au serveur.

    Ouvrez un nouveau terminal et saisissez ce qui suit:

    postman

    La commande  postman entraînera l'affichage de la page ci-dessous par votre navigateur Web:

    postier_inscription

    Vous pouvez décider de vous inscrire et de créer un compte gratuit, mais nous ignorerons et aurons un accès direct à l'application pour tester l'API de la bibliothèque comme indiqué ci-dessous:

    Tester l'API de la bibliothèque

     

    Dans cette section, nous autoriserons un utilisateur à s'inscrire à l'API de la bibliothèque en fournissant un nom d'utilisateur et un mot de passe unique au format JSON en utilisant la méthode POST en suivant les étapes ci-dessous:

    • Cliquez sur l'onglet intitulé Body
    • Sélectionnez ensuite le bouton brut et choisissez le format JSON
    • entrez un nom d'utilisateur et un mot de passe pour vous inscrire comme indiqué sur la capture d'écran
    • À côté du bouton d'envoi, insérez l'URL suivante http://127.0.0.1/register
    • Enfin, changez la méthode en POST et appuyez sur le bouton d'envoi.

    un utilisateur s'inscrit pour une API

    Il affichera la sortie suivante comme indiqué ci-dessous:

    Nous avons maintenant enregistré un utilisateur avec succès. Allons-y pour permettre à l'utilisateur qui vient de s'inscrire de se connecter afin de générer un jeton aléatoire temporaire pour accéder à la table Auteurs en utilisant les étapes suivantes:

    •  Cliquez sur l'onglet d'autorisation.
    • Dans la section type, sélectionnez l'authentification de base.
    • Remplissez ensuite le formulaire de nom d'utilisateur et de mot de passe avec le nom d'utilisateur et le mot de passe avec lesquels vous vous êtes enregistré précédemment.
    • Enfin, appuyez sur le bouton d'envoi pour vous connecter et générer un jeton aléatoire.

    Une fois que l'utilisateur s'est connecté avec succès, un jeton aléatoire est généré pour l'utilisateur, comme indiqué dans la capture d'écran.

    Nous utiliserons le jeton aléatoire généré pour accéder à la table des auteurs.

    Dans cette section, nous ajouterons les informations d'un auteur à la table Authors via la méthode POST en suivant les étapes suivantes:

    • Cliquez sur l'onglet des en-têtes
    • Incluez les en-têtes HTTP suivants affichés dans la capture d'écran

    • Ensuite, cliquez sur l'onglet corps et entrez les détails du nouvel auteur
    • Appuyez ensuite sur le bouton d'envoi pour ajouter les détails de l'auteur au tableau de l'auteur

    Vous pouvez également récupérer les informations sur les auteurs dans le tableau Auteurs via les éléments suivants:

    • Assurez-vous que votre jeton généré se trouve dans la section des en-têtes. s'il n'y est pas, vous devez le remplir avec votre token.
    • À côté du bouton d'envoi, saisissez cette URL http://127.0.0.1/authors
    • Changez ensuite la méthode HTTP en GET et appuyez sur le bouton d'envoi pour récupérer les détails des auteurs.

    Enfin, vous pouvez supprimer le (s) auteur (s) dans le tableau Auteurs via le DELETE méthode en utilisant les étapes suivantes:

    • Assurez-vous que votre jeton est toujours dans la section des en-têtes. Vous pouvez vérifier l'onglet des en-têtes pour vous assurer que les informations nécessaires sont en place.
    • À côté du bouton d'envoi, saisissez cette URL http://127.0.0.1/sam
    • Appuyez ensuite sur le bouton d'envoi pour supprimer l'utilisateur que vous avez spécifié.

    Vous pouvez trouver le code source complet sur Github.  Vous pouvez le cloner et le vérifier sur votre machine.