Geekflare est soutenu par notre public. Nous pouvons gagner des commissions d'affiliation en achetant des liens sur ce site.
Partager sur:

Comment sécuriser une API REST Flask avec un jeton Web JSON?

geekécrire
Scanner de sécurité des applications Web Invicti – la seule solution qui offre une vérification automatique des vulnérabilités avec Proof-Based Scanning™.

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-il signé ou non signé JWT?
  • 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.

À partir 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 Auteurs

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.

Testing the library API with 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.

Merci à nos commanditaires
Plus de bonnes lectures sur le développement
Alimentez votre entreprise
Certains des outils et services pour aider votre entreprise à se développer.
  • Invicti utilise Proof-Based Scanning™ pour vérifier automatiquement les vulnérabilités identifiées et générer des résultats exploitables en quelques heures seulement.
    Essayez Invicti
  • Web scraping, proxy résidentiel, proxy manager, web unlocker, moteur de recherche et tout ce dont vous avez besoin pour collecter des données Web.
    Essayez Brightdata
  • Semrush est une solution de marketing numérique tout-en-un avec plus de 50 outils de référencement, de médias sociaux et de marketing de contenu.
    Essayez Semrush
  • Intruder est un scanner de vulnérabilités en ligne qui détecte les failles de cybersécurité de votre infrastructure, afin d'éviter des violations de données coûteuses.
    Essayez Intruder