flawopen.com/SQL Injection en Python/Django ORM
L’ORM Django protège-t-il contre SQL Injection ?
L’API QuerySet de Django sépare le SQL des valeurs et transmet celles-ci comme paramètres au pilote de base de données pour les requêtes courantes, telles que .filter(), .get() et .exclude(). Cette protection ne couvre pas toutes les façons de construire une requête : SQL brut, expressions personnalisées et structure choisie par l’utilisateur nécessitent un examen distinct.
Utiliser les valeurs en sécurité avec les QuerySets Django
username = request.GET.get("u", "")
User.objects.raw(
f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
username = request.GET.get("u", "")
User.objects.raw(
"SELECT * FROM myapp_user WHERE username = %s",
[username],
)
Les limites de la protection
Manager.raw() exécute le SQL fourni. Transmettez les valeurs contrôlées par l’utilisateur via params et laissez les marqueurs sans guillemets, par exemple User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). N’insérez pas de valeurs avec une f-string, le formatage % ou la concaténation. Les paramètres représentent des valeurs, pas des identifiants SQL comme les noms de tables ou de colonnes.
Pour accéder directement à la base, utilisez cursor.execute(sql, params) et transmettez les valeurs séparément avec les marqueurs documentés par Django. N’interpolez pas de valeurs dans sql. Examinez aussi RawSQL et les expressions Func : le contexte du modèle et les arguments nommés peuvent être interpolés dans le SQL au lieu d’être liés comme paramètres.
Un chemin de champ contrôlé par l’utilisateur dans .filter(**{field: value}) n’est pas automatiquement une SQL Injection : Django résout les noms de lookup comme structure de requête. Il peut toutefois exposer des champs, relations ou lookups que l’endpoint ne devrait pas proposer, et provoquer des erreurs. Convertissez les choix externes de tri et de filtre en liste d’autorisation limitée de champs et lookups.
Auditer les requêtes Django
rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' ..raw(), RawSQL, .extra() et connection.cursor(). Examinez chaque occurrence et remontez les valeurs jusqu’à la requête : paramètres liés, fragments SQL non fiables, identifiants ou lookups dynamiques, et modèles personnalisés Func.Questions fréquentes
QuerySet.extra() est une API historique qui ajoute des fragments SQL. Évitez-la dans le nouveau code et examinez attentivement ses arguments where et select. Préférez les expressions QuerySet ; si le SQL brut est nécessaire, liez les valeurs avec l’argument documenté params.
Django REST Framework ne change pas la paramétrisation SQL de l’ORM. Les mêmes règles s’appliquent aux requêtes issues des vues, serializers et viewsets ; limitez aussi par une liste d’autorisation les champs de tri ou de filtre contrôlés par le client.
Références
- Django docs — SQL injection protection
- flawopen.com — SQL Injection in Python
- Django docs — Performing raw SQL queries
- Django docs — Query expressions and RawSQL