name: inverse class: inverse template: inverse layout: true --- template: inverse class: center, middle # Sécurité WEB --- layout: false class: middle, center .image[![hashbang](img/hashbang.png)] --- layout: false class: agenda # Sommaire
-- * Injection de gabarit HTML -- * XSS classiques dans django/flask ... -- * Les formulaires génériques -- * Injection de commande -- * `assert False` en production -- * Mauvaise sérialisation -- * Nginx -- * Mentions spéciales -- * Résumé --- template: inverse class: center, middle ## Injection de gabarit HTML --- # Injection de gabarit HTML Problème : laisser l'utilisateur modifier les gabarits (django templates, Jinja2 templates..) -- Impact : variable, de la fuite d'information à l'execution de commandes sur le serveur vulnérable -- Solution : ne PAS autoriser l'utilisateur à modifier le gabarit HTML. Passer les variables en contexte --- template: inverse class: center, middle # XSS classiques dans django, flask... --- # Des XSS classiques ```html
Profil de {{ user.name }}
{{ user.website }}
``` -- Problème ? --- # Des XSS classiques Si `user.avatar` = `"x onerror=alert(0)"` -- ```html
``` -- ```html
``` -- Nous avons donc une XSS (éxécution arbitraire de JS) --- # Des XSS classiques ```html
Profil de {{ user.name }}
{{ user.website }}
``` --- # Des XSS classiques ```html
Profil de {{ user.name }}
{{ user.website }}
``` -- Problème ? --- # Des XSS classiques Si `user.website` = `"javascript:alert(0)"` -- ```html
{{ user.website }}
``` -- ```html
javascript:alert(0)
``` -- Nous avons à nouveau une éxécution arbitraire de JS --- # Des XSS classiques Correction ? -- Vérification du schema des URLs utilisateur : `http` ou `https` --- # XSS - récapitulation * Utiliser les protections basiques : `{{ user.name }}` * Toujours entourer les attributs HTML de guillements * Vérifier le schema des URLs utilisateur : `http` ou `https` --- template: inverse class: center, middle # La magie des framework --- # Magie des frameworks Les formulaires et vues génériques peuvent être trop permissives ```python class UserCreateView(CreateView): model = User fields = '__all__' ``` -- Solution : limiter avec une **whitelist** ce que l'utilisateur peut éditer ```python class UserCreateView(CreateView): model = User fields = ['username', 'email'] ``` -- De manière générale, les frameworks essaient de limiter les erreurs possibles par un développeur mais ne peuvent pas tout supprimer --- template: inverse class: center, middle # Injection de commande --- # Injection de commande ```python command = input() result = subprocess.check_output( f"sqlite3 ./db.sqlite3 '{command}'", shell=True, ) ``` -- Si command = `' ; ls -la '.`, l'utilisateur peut éxécuter du code arbitraire --- # Injection de commande Première amélioration : ```python command = input() result = subprocess.check_output( ["sqlite3", "./db.sqlite3", command] ) ``` -- Mais toujours pas parfait : si command = `.shell ls -la` nous avons encore le même soucis --- # Injection de commande Solution finale ```python command = input() assert ".shell" not in command result = subprocess.check_output( ["sqlite3", "./db.sqlite3", command] ) ``` Vérifier l'input utilisateur avant de lancer des commandes système ! -- Solution *finale* ? Vraiment ? --- template: inverse class: center, middle # Je t'`assert` --- # `assert` Doc python : ```python " The current code generator emits no code for an assert statement when optimization is requested at compile time. " ``` -- Donc si le serveur est lancé en demandant à python d'activer des optimisations, les assert n'ont plus **aucun** effet ! -- Vous pouvez tester avec : `PYTHONOPTIMIZE=1 python manage.py runserver` --- template: inverse class: center, middle # Mauvaises sérialisations --- # Mauvaises sérialisations ```python import pickle pickle.loads(user_input) ``` -- À ne pas utiliser si possible ! --- # Mauvaises sérialisations ```python import yaml yaml.loads(user_input) ``` -- ```python import yaml yaml.safe_loads(user_input) ``` --- template: inverse class: center, middle # Config nginx --- # Config nginx ```nginx upstream python_uwsgi { server unix:///path/to/uwsgi.sock; } server { [...] location /static { alias /path/to/project/static/; [...] } location / { uwsgi_pass python_uwsgi; [...] } } ``` --- # Config nginx ```nginx upstream python_uwsgi { server unix:///path/to/uwsgi.sock; } server { [...] * location /static { * alias /path/to/project/static/; [...] } location / { uwsgi_pass python_uwsgi; [...] } } ``` --- # Config nginx ```nginx server { [...] * location /static { * alias /path/to/project/static/; [...] } } ``` Requêter **https://example.com/static.red[/asset.png]** -- Rendra le fichier **/path/to/project/static/.red[/asset.png]** -- La configuration nginx semble donc opérationnelle --- # Config nginx ```nginx server { [...] * location /static { * alias /path/to/project/static/; [...] } } ``` Requêter **https://example.com/static.red[../app.py]** -- Rendra le fichier **/path/to/project/static/.red[../app.py]** -- Et donc le fichier **/path/to/project/.red[app.py]** --- # Config nginx - Impact * Accès au code source de l'application -- * Accès aux mots de passes et secrets (comme par exemple `SECRET_KEY` de Django) -- * Potentiel accès aux fichiers d'autres utilisateurs --- # Config nginx - corrigé ```nginx server { [...] * location /static/ { * alias /path/to/project/static/; [...] } } ``` OU ```nginx server { [...] * location /static { * alias /path/to/project/static; [...] } } ``` `location` et `alias` doivent correspondre --- template: inverse class: center, middle # Mentions spéciales --- # Mentions spéciales -- * python `import random` -- => en général, utiliser `import secrets` -- * CSRF dans ces progiciels -- => respectez les ! --- template: inverse class: center, middle # Résumé --- # Résumé * Ne pas faire confiance aux inputs utilisateur -- * Lire la documentation de votre lib (yaml/pickle) ou framework (django/flask) -- * Relire la documentation -- * Principe de moindre privilège, n'autorisez que ce **que** vous devez -- * Faites des revues de code 👍 --- template: inverse class: center, middle # Merci ! sources : [Application de démo](example.zip) --- class: middle, center .image[![hashbang](img/hashbang.png)] ### hugo@hashbang.fr