Sécurité (Flask)
La sécurité dans Flask regroupe l’ensemble des mesures et bonnes pratiques visant à protéger une application web contre les attaques, les accès non autorisés et les fuites de données.
Comme Flask est un framework minimaliste, il ne fournit pas toutes les protections par défaut, ce qui impose au développeur d’intégrer manuellement ou via des extensions les fonctionnalités de sécurité nécessaires.
Cela inclut :
-
Protéger les données des utilisateurs (authentification, chiffrement).
-
Empêcher les attaques courantes (XSS, CSRF, injection SQL, etc.).
-
Contrôler l’accès aux ressources.
Comment ça fonctionne dans Flask
Authentification et autorisation
-
Authentification : vérifier l’identité d’un utilisateur (ex. : via un formulaire de connexion, JWT, OAuth2).
-
Autorisation : déterminer si l’utilisateur a le droit d’effectuer une action.
-
Extensions utiles :
-
Flask-Login
pour gérer les sessions utilisateur. -
Flask-JWT-Extended
pour sécuriser les API avec des tokens JWT. -
Flask-Security
pour un package tout-en-un (login, rôles, permissions).
-
Protection contre les attaques courantes
-
CSRF (Cross-Site Request Forgery) :
-
Utiliser
Flask-WTF
pour générer des jetons CSRF dans les formulaires.
-
-
XSS (Cross-Site Scripting) :
-
Échapper toutes les données affichées dans les templates Jinja2 (par défaut).
-
-
Injection SQL :
-
Utiliser un ORM comme SQLAlchemy au lieu de requêtes SQL brutes.
-
-
Clickjacking :
-
Ajouter l’en-tête HTTP
X-Frame-Options: DENY
.
-
Sécurisation des communications
-
Utiliser HTTPS pour chiffrer les échanges (via un certificat SSL/TLS).
-
Rediriger automatiquement tout trafic HTTP vers HTTPS.
-
Protéger les cookies avec :
-
Secure
(uniquement via HTTPS) -
HttpOnly
(non accessible via JavaScript) -
SameSite
(empêche certaines attaques CSRF).
-
Gestion des erreurs et des logs
-
Ne pas afficher de messages d’erreurs détaillés en production (
DEBUG = False
). -
Consigner les événements suspects dans un fichier log pour analyse.
-
Utiliser un système d’alerte si des tentatives d’intrusion sont détectées.
Exemple conceptuel
-
URL / endpoint :
/dashboard
-
Comportement :
-
Si l’utilisateur n’est pas authentifié, redirection vers
/login
. -
Si l’utilisateur est connecté mais n’a pas le rôle requis, renvoyer 403 Forbidden.
-
Si tout est correct, afficher le tableau de bord.
-
-
Protection :
-
Formulaire de login avec jeton CSRF.
-
Session sécurisée avec cookies
Secure
etHttpOnly
.
-
Bonnes pratiques
-
Toujours désactiver le mode debug en production.
-
Mettre à jour Flask et toutes les dépendances pour éviter les failles connues.
-
Éviter de stocker des mots de passe en clair (utiliser
bcrypt
ouwerkzeug.security
pour les hasher). -
Valider et filtrer toutes les données entrantes.
-
Limiter le nombre de tentatives de connexion (brute force protection).
-
Surveiller les journaux et mettre en place un système de détection d’intrusion.
Pourquoi c’est important
-
Une application Flask mal sécurisée peut être facilement compromise.
-
La sécurité protège non seulement les utilisateurs mais aussi la réputation et les données de l’entreprise.
-
Respecter les normes de sécurité est souvent une obligation légale (RGPD, HIPAA, PCI-DSS…).
En résumé
-
Flask fournit la base, mais la sécurité doit être intégrée via extensions et bonnes pratiques.
-
Authentification, autorisation et protection contre les attaques courantes sont indispensables.
-
Les communications doivent être chiffrées et les données protégées.
-
Une bonne gestion des erreurs et des logs aide à prévenir et analyser les incidents.
Fonctions :
-
Sécurité avec Flask
Exemple de code :
from flask import Flask, request, session, redirect, url_for, jsonify from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) # Clé secrète pour sécuriser les sessions (à garder privée !) app.secret_key = "une_cle_secrete_tres_complexe" # Simulation d'une "base de données" utilisateur users_db = { "alice": generate_password_hash("motdepasse123"), "bob": generate_password_hash("azerty") } # Route d'accueil @app.route("/") def home(): if "username" in session: return f"Bienvenue {session['username']} ! <br><a href='/logout'>Se déconnecter</a>" return "Bienvenue sur le site ! <br><a href='/login'>Se connecter</a>" # Route de connexion @app.route("/login", methods=["GET", "POST"]) def login(): if request.method == "POST": username = request.form.get("username") password = request.form.get("password") if username in users_db and check_password_hash(users_db[username], password): session["username"] = username return redirect(url_for("home")) else: return "Identifiants invalides !", 401 return ''' <form method="post"> Nom d'utilisateur : <input type="text" name="username"><br> Mot de passe : <input type="password" name="password"><br> <input type="submit" value="Se connecter"> </form> ''' # Route protégée (accessible uniquement aux utilisateurs connectés) @app.route("/secret") def secret(): if "username" not in session: return redirect(url_for("login")) return jsonify({"message": f"Voici le contenu secret réservé à {session['username']} !"}) # Route de déconnexion @app.route("/logout") def logout(): session.pop("username", None) return redirect(url_for("home")) if __name__ == "__main__": app.run(debug=True)
Explication du code :
from flask import Flask, request, session, redirect, url_for, jsonify
importe les modules Flask nécessaires : Flask pour créer l’application,request
pour accéder aux données envoyées par l’utilisateur,session
pour gérer les sessions sécurisées,redirect
eturl_for
pour les redirections, etjsonify
pour retourner des réponses JSON.from werkzeug.security import generate_password_hash, check_password_hash
importe des fonctions pour sécuriser les mots de passe : -generate_password_hash
pour créer un hash sécurisé d’un mot de passe. -check_password_hash
pour vérifier si un mot de passe correspond au hash stocké.app = Flask(__name__)
crée une instance de l’application Flask.app.secret_key = "une_cle_secrete_tres_complexe"
définit une clé secrète utilisée par Flask pour sécuriser les sessions et les cookies. Cette clé doit rester confidentielle.1️⃣ Simulation d’une "base de données" utilisateur
users_db = { "alice": generate_password_hash("motdepasse123"), "bob": generate_password_hash("azerty") }
crée un dictionnaire simulant une base de données d’utilisateurs, avec les mots de passe stockés sous forme de hash sécurisé.2️⃣ Route d’accueil
@app.route("/")
définit la route principale.
La fonctionhome()
vérifie si un utilisateur est connecté ("username" in session
) : - Si oui → affiche un message de bienvenue et un lien pour se déconnecter. - Si non → affiche un lien pour se connecter.3️⃣ Route de connexion
@app.route("/login", methods=["GET", "POST"])
définit une route qui accepte les méthodes GET et POST pour gérer le formulaire de connexion.- Si la méthode est POST : on récupère
username
etpassword
envoyés par le formulaire. - On vérifie si l’utilisateur existe dans
users_db
et si le mot de passe correspond au hash stocké (check_password_hash
). - Si la vérification réussit → on stocke le nom d’utilisateur dans
session["username"]
et on redirige vershome()
. - Si la vérification échoue → on renvoie un message d’erreur avec le code HTTP 401 (Unauthorized).
- Si la méthode est GET → on retourne un formulaire HTML simple pour saisir le nom d’utilisateur et le mot de passe.
4️⃣ Route protégée
@app.route("/secret")
définit une route accessible uniquement aux utilisateurs connectés.
La fonctionsecret()
vérifie si"username"
est présent danssession
: - Si non → redirection vers la page de connexion. - Si oui → renvoie un message JSON contenant le contenu secret pour l’utilisateur connecté.5️⃣ Route de déconnexion
@app.route("/logout")
définit une route pour se déconnecter.
La fonctionlogout()
supprime la clé"username"
de la session avecsession.pop("username", None)
et redirige vers la page d’accueil.6️⃣ Lancer l’application Flask
if __name__ == "__main__":
vérifie que le fichier est exécuté directement.
app.run(debug=True)
démarre le serveur Flask en mode debug, accessible par défaut sur http://127.0.0.1:5000/. Le mode debug permet le rechargement automatique et l’affichage détaillé des erreurs. - Si la méthode est POST : on récupère