Sécuriser une application Flask : Un Tutoriel approfondi
Pourquoi Securiser une application Flask ?
Dans un monde où la sécurité est devenue une priorité croissante, il est crucial que les développeurs soient conscients des risques liés à l'application web Flask. Le développement d'une application Flask peut sembler simple et ludique au début, mais elle peut rapidement devenir vulnérable aux attaques si elle n'est pas correctement sécurisée.
Un cas concret de situation est lorsqu'un développeur crée un site web pour une entreprise qui gère des informations sensibles comme les données clients ou les paiements. Si ce site n'est pas sécurisé, il peut être facilement piraté et toutes ces informations compromises.
Prerequis
- Connaissance de base en Python
- Connaissance de la structure d'une application Flask
- Installation de Flask :
pip install flask - Installation de Werkzeug pour les tests :
pip install werkzeug
Concepts fondamentaux
1. Authentification et Autorisation
L'authentification est le processus par lequel un utilisateur prouve son identité, tandis que l'autorisation consiste à déterminer quelles actions un utilisateur peut effectuer une fois qu'il a été authentifié.
## Importation des modules nécessaires
from flask import Flask, request, session, redirect, url_for, render_template_string
app = Flask(__name__)
app.secret_key = 'supersecretkey'
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# Vérification des identifiants (pour le but de ce tutoriel)
if username == 'admin' and password == 'secret':
session['user'] = username
return redirect(url_for('home'))
else:
return 'Nom d\'utilisateur ou mot de passe incorrect'
return render_template_string('''
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
''')
@app.route('/home')
def home():
if 'user' in session:
return f'Welcome {session["user"]}!'
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
2. Protection contre les injections SQL
Les injections SQL sont une des vulnérabilités les plus courantes dans les applications web.
## Importation des modules nécessaires
from flask import Flask, request
import sqlite3
app = Flask(__name__)
@app.route('/search', methods=['GET'])
def search():
query = request.args.get('query')
# Injection SQL potentielle (pour le but de ce tutoriel)
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM users WHERE name LIKE '%{query}%'")
results = cursor.fetchall()
conn.close()
return str(results)
if __name__ == '__main__':
app.run(debug=True)
3. Protection contre les cross-site scripting (XSS)
Les attaques XSS se produisent lorsque le code HTML ou JavaScript est injecté dans une page web que l'utilisateur peut ensuite exécuter.
## Importation des modules nécessaires
from flask import Flask, request, escape
app = Flask(__name__)
@app.route('/greet', methods=['GET'])
def greet():
name = request.args.get('name')
# Injection XSS potentiel (pour le but de ce tutoriel)
return f'<h1>Hello {name}</h1>'
if __name__ == '__main__':
app.run(debug=True)
4. Protection contre les attaques CSRF
Les attaques CSRF se produisent lorsque l'utilisateur est trompé en cliquant sur un lien malveillant qui envoie une requête non autorisée à une application.
## Importation des modules nécessaires
from flask import Flask, request, session, redirect, url_for, render_template_string
from flask_wtf.csrf import CSRFProtect
app = Flask(__name__)
app.secret_key = 'supersecretkey'
csrf = CSRFProtect(app)
@app.route('/transfer', methods=['GET'])
def transfer():
return render_template_string('''
<form method="post" action="/process_transfer">
Amount: <input type="text" name="amount"><br>
Destination: <input type="text" name="destination"><br>
<input type="submit" value="Transfer">
</form>
''')
@app.route('/process_transfer', methods=['POST'])
def process_transfer():
amount = request.form['amount']
destination = request.form['destination']
# Traitement du transfert (pour le but de ce tutoriel)
return f'Transfer of {amount} to {destination} processed.'
if __name__ == '__main__':
app.run(debug=True)
Mise en pratique : Projet fil rouge
Mini-Projet : Gestionnaire de Tâches
Ce projet implémentera un simple gestionnaire de tâches avec des fonctionnalités de connexion, d'ajout et de suppression de tâches.
## Importation des modules nécessaires
from flask import Flask, request, render_template_string, session, redirect, url_for
app = Flask(__name__)
app.secret_key = 'supersecretkey'
tasks = []
@app.route('/')
def index():
return render_template_string('''
<h1>Gestionnaire de Tâches</h1>
<a href="/login">Login</a> | <a href="/register">Register</a>
{% if session.get("user") %}
<a href="/logout">Logout</a>
<ul>
{% for task in tasks %}
<li>task</li>
{% endfor %}
</ul>
<form method="post" action="/add_task">
Task: <input type="text" name="task"><br>
<input type="submit" value="Add Task">
</form>
{% endif %}
''')
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# Vérification des identifiants (pour le but de ce tutoriel)
if username == 'admin' and password == 'secret':
session['user'] = username
return redirect(url_for('index'))
else:
return 'Nom d\'utilisateur ou mot de passe incorrect'
return render_template_string('''
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Login">
</form>
''')
@app.route('/logout')
def logout():
session.pop('user', None)
return redirect(url_for('index'))
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
# Enregistrement des utilisateurs (pour le but de ce tutoriel)
session['user'] = username
return redirect(url_for('index'))
return render_template_string('''
<form method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<input type="submit" value="Register">
</form>
''')
@app.route('/add_task', methods=['POST'])
def add_task():
task = request.form['task']
tasks.append(task)
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
Erreurs frequentes et debugging
1. Injection SQL
## ❌ Mauvais
def get_user(username):
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
cursor.execute(f"SELECT * FROM users WHERE username = '{username}'")
user = cursor.fetchone()
conn.close()
return user
## ✅ Correct
from flask_sqlalchemy import SQLAlchemy
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///example.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
def get_user(username):
user = User.query.filter_by(username=username).first()
return user
2. XSS
## ❌ Mauvais
def greet(name):
return f'<h1>Hello {name}</h1>'
## ✅ Correct
from flask import escape
def greet(name):
return f'<h1>Hello {escape(name)}</h1>'
3. CSRF
## ❌ Mauvais
@app.route('/transfer', methods=['GET'])
def transfer():
return render_template_string('''
<form method="post" action="/process_transfer">
Amount: <input type="text" name="amount"><br>
Destination: <input type="text" name="destination"><br>
<input type="submit" value="Transfer">
</form>
''')
## ✅ Correct
@app.route('/transfer', methods=['GET'])
def transfer():
return render_template_string('''
<form method="post" action="/process_transfer">
Amount: <input type="text" name="amount"><br>
Destination: <input type="text" name="destination"><br>
CSRF Token: <input type="hidden" name="csrf_token" value="csrf_token()">
<input type="submit" value="Transfer">
</form>
''')
Pour aller plus loin
1. Utilisation de Flask-HTTPAuth pour l'authentification
Flask-HTTPAuth est une extension qui facilite l'ajout d'authentification HTTP à des applications Flask.
2. Utilisation de Flask-Security pour la gestion de l'utilisateur
Flask-Security offre des fonctionnalités comme le register, login, logout et mot de passe oublié.
3. Sécurité de la communication HTTPS
Utilisez HTTPS pour sécuriser la communication entre le client et le serveur.
Défi pratique : Sécurisation d'une API RESTful
Implémentez une API RESTful sécurisée avec Flask, incluant l'authentification JWT et la protection contre les attaques CSRF.
Tuto sur API sécurisée avec Flask
Ce tutoriel a couvert de nombreux aspects importants de la sécurité d'une application Flask. En suivant ces concepts et en appliquant les bonnes pratiques, vous pouvez construire des applications web sécurisées pour protéger vos utilisateurs et leurs données sensibles.