Les différentes méthodes de sécurisation
Il existe plusieurs moyens de sécuriser son code Python, voici quelques exemples :
- Validation des entrées : Il est important de vérifier que les entrées à votre programme sont valides et attendues. Par exemple, si votre programme accepte une adresse IP en entrée, assurez-vous qu’elle est bien au format IP valide avant de l’utiliser.
- Échappement des caractères sensibles : Si vous utilisez des entrées dans des commandes système ou SQL, assurez-vous de les échapper correctement pour éviter les attaques par injection.
- Utilisation de bibliothèques de confiance : Utilisez des bibliothèques de confiance qui ont été largement testées et approuvées par la communauté. Évitez d’utiliser des bibliothèques non fiables ou non mises à jour.
- Cryptage des données sensibles : Si vous traitez des informations sensibles telles que des mots de passe ou des numéros de carte de crédit, assurez-vous de les crypter avant de les stocker ou de les transmettre.
- Utilisez des outils de protection de la mémoire : Utilisez des outils pour protéger contre les fuites de mémoire et les accès non autorisés à la mémoire.
- Utilisez des outils de détection de vulnérabilités : Utilisez des outils pour détecter les vulnérabilités dans votre code, comme par exemple bandit pour Python, qui peut aider à identifier les problèmes de sécurité potentiels dans votre code.
- Utiliser la gestion des exceptions: Utiliser des try/except pour capturer les exceptions et gérer les erreurs, permet d’éviter d’avoir des problème de sécurité lié à une utilisation non prévue de votre programme.
Il est important de noter que la sécurisation de code est un domaine en constante évolution et il est important de rester informé des dernières vulnérabilités et des meilleures pratiques pour sécuriser votre code.
Sécuriser son code Python en validant les entrées
Utilisation des expressions régulières
Vous pouvez utiliser la bibliothèque re
pour vérifier si une entrée correspond à un format spécifié en utilisant des expressions régulières. Par exemple, pour vérifier si une entrée est une adresse email valide :
import re def is_valid_email(email): pattern = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$") return pattern.match(email) email = "user@example.com" if is_valid_email(email): print("Email est valide.") else: print("Email n'est pas valide.")
Utiliser une fonction de validation
def is_valid_integer(n): try: int(n) return True except ValueError: return False num = "123" if is_valid_integer(num): print("c'est un nombre entier.") else: print("ce n'est pas un nombre entier.")
Utiliser les bibliothèques pour la validation de vos données Python
from cerberus import Validator v = Validator() document = {"name": "John", "age": 30, "email": "john@example.com"} schema = {"name": {"type": "string"}, "age": {"type": "integer"}, "email": {"type": "string", "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"}} if v.validate(document, schema): print("document est valide.") else: print("document n'est pas valide.")
Échapper les caractères sensibles dans votre code
Lorsque vous utilisez des entrées dans une requête SQL, il est important de les échapper pour éviter une injection. Pour cela, vous pouvez utiliser les méthodes quote()
ou escape()
fournies par les bibliothèques de base de données pour échapper les entrées.
import mysql.connector def execute_query(query): cnx = mysql.connector.connect(user='root', password='password', host='127.0.0.1', database='mydb') cursor = cnx.cursor() cursor.execute(query) cnx.commit() cursor.close() cnx.close() name = "John'; DROP TABLE users; --" query = "SELECT * FROM users WHERE name = '{}'".format(mysql.connector.escape(name)) execute_query(query)
Utiliser la bibliothèque shlex pour échapper vos entrées
import shlex import subprocess def execute_command(cmd): cmd = " ".join(shlex.quote(x) for x in cmd.split()) subprocess.run(cmd, shell=True, check=True) cmd = "rm -rf /; echo 'done'" execute_command(cmd)
Protéger votre code Python en cryptant vos données sensibles
La bibliothèque cryptography
La bibliothèque cryptography est souvent utilisée pour crypter ou décrypter des données en Python. Elle prend en charge une multitude d’algorithmes de chiffrement tel que AES, RSA etc … Cette bibliothèque crypte vos données de manière symetrique (clé identique pour le chiffrement / déchiffrement) ou asymétrique (une clé publique et une clé privée)
from cryptography.fernet import Fernet # generate a key key = Fernet.generate_key() print(f'key: {key}') cipher_suite = Fernet(key) cipher_text = cipher_suite.encrypt(b"This is a top secret message.") print(f'cipher_text: {cipher_text}') plain_text = cipher_suite.decrypt(cipher_text) print(f'plain_text: {plain_text}')
La bibliothèque pycrypto
pycrypte permet d’utiliser les fonctionnalités de chiffrement comme par exemple : AES, DES, RSA. Son utilisation permet le cryptage de données symétrique.
from Crypto.Cipher import AES import os key = os.urandom(AES.block_size) cipher = AES.new(key) plaintext = b'This is a top secret message.' ciphertext = cipher.encrypt(plaintext) print(f'ciphertext: {ciphertext}') # decryption cipher = AES.new(key) decryptedtext = cipher.decrypt(ciphertext) print(f'decryptedtext: {decryptedtext}')
La bibliothèque pyOpenSSL
Elle permet l’utilisation de fonctionnalités de chiffrement RSA,DSA,DH , cette dernière est utilisée pour le cryptage asymétrique
from OpenSSL import crypto private_key = crypto.PKey() private_key.generate_key(crypto.TYPE_RSA, 2048) public_key = private_key.publickey() # encrypt ciphertext = crypto.m2crypto.m2.rsa_public_encrypt(b'This is a top secret message.', public_key) print(f'ciphertext: {ciphertext}') # decrypt plaintext = crypto.m2crypto.m2.rsa_private_decrypt(ciphertext, private_key) print(f'plaintext: {plaintext}')
Protéger votre code Python des fuites de mémoire
La bibliothèque ctypes
permet de gérer les pointeurs et les types de données en C, ce qui vous permet de contrôler la mémoire de manière fine. Vous pouvez utiliser les fonctions de mémoire memset()
et memset_s()
pour remplir la mémoire avec des valeurs nulles, ce qui peut aider à protéger les données sensibles.
from ctypes import CDLL, POINTER, c_char, c_size_t libc = CDLL('libc.so.6') def memset_s(s, smax, c, n): libc.memset_s(s, smax, c, n) data = "my secret data" data_ptr = c_char.from_buffer(data) memset_s(data_ptr, len(data), 0, len(data)) print(f'data: {data}')
La bibliothèque numpy
numpy permet la création et la manipulation de tableaux de données. Il est possible d’utiliser numpy.memmap pour créer des tableaux mappés en mémoire
import numpy as np data = np.memmap('my_file', dtype='float32', mode='w+', shape=(100,100)) data[:] = np.random.randn(*data.shape) # clear data del data
Utiliser la librairie bandit pour sécuriser son code Python
Bandit est un outil de sécurité pour Python qui peut aider à identifier les problèmes de sécurité potentiels dans votre code. Il peut détecter les vulnérabilités telles que les injections SQL, les problèmes de gestion des erreurs, les fuites de mémoire et les problèmes de cryptographie. Il est open-source et maintenu par l’équipe de sécurité de OpenStack.
Installez Bandit en utilisant pip
python -m pip install bandit
Utilisez la commande bandit pour lancer une analyse sur un fichier ou un répertoire:
python -m bandit -r my_code_directory
Les résultats de l’analyse seront affichés dans le terminal, y compris le nom de la vulnérabilité détectée, sa gravité, et la ligne de code où elle a été détectée.
utiliser les options de Bandit Python
python -m bandit -r my_code_directory -ll -ii
-ll : permet de limiter les résultats à certains niveaux de gravité
-ii : permet de limiter les résultats à certains contextes d’analyse
Il est également possible d’intégrer Bandit dans des outils de construction ou de continuer l’intégration pour automatiser les tests de sécurité lors de chaque build.
Il est important de noter que Bandit ne garantit pas une sécurisation complète de votre code, mais il peut vous aider à identifier les problèmes potentiels de sécurité. Il est donc important de comprendre les résultats de l’analyse et de les corriger si nécessaire.
Outre la validation des entrées, l’échappement des caractères sensibles, l’utilisation de bibliothèques de confiance et le cryptage des données, une autre pratique essentielle pour sécuriser votre code Python est la gestion sûre des dépendances. Les dépendances de votre projet sont autant de portes d’entrée possibles pour des vulnérabilités, et il est crucial de s’assurer qu’elles sont à jour et dépourvues de failles connues.
- Utiliser un environnement virtuel :Travailler dans un environnement virtuel, comme celui créé par
venv
ouvirtualenv
, peut aider à isoler vos dépendances. Cela évite les conflits entre les versions de paquets et vous permet de tester les mises à jour de sécurité sans affecter l’environnement global. - Vérifier régulièrement les mises à jour des paquets :Utilisez des outils tels que
pip list --outdated
pour détecter les paquets qui nécessitent des mises à jour. Les nouvelles versions peuvent corriger des vulnérabilités de sécurité critiques. - Analyser les dépendances pour les vulnérabilités :Des outils comme
Safety
,Snyk
ouPyUp
scannent automatiquement vos fichiersrequirements.txt
ouPipfile.lock
à la recherche de dépendances connues pour leurs failles de sécurité. - Maîtriser les paquets sources :Préférez les paquets provenant de PyPI officiel ou d’autres dépôts de confiance. Soyez méfiant envers les paquets qui ne sont pas largement utilisés ou qui semblent être abandonnés, car ils peuvent contenir des failles de sécurité non corrigées.
Voici un exemple de script pour analyser les vulnérabilités des dépendances :
# Script pour vérifier les vulnérabilités dans les dépendances Python from safety import safety from pip._internal.utils.misc import get_installed_distributions def check_vulnerabilities(): packages = get_installed_distributions() packages = [{'key': pkg.key, 'version': pkg.version} for pkg in packages] vulnerabilities = safety.check(packages=packages) if vulnerabilities: print("Vulnérabilités détectées !") for vulnerability in vulnerabilities: print(f"{vulnerability['package']}=={vulnerability['installed_version']}") print(f"Détails de la vulnérabilité : {vulnerability['vuln']}") else: print("Aucune vulnérabilité détectée.") check_vulnerabilities()
Mises en place d’un processus d’audit régulier
L’audit de sécurité ne doit pas être une action ponctuelle. Il est conseillé d’établir un processus d’audit régulier pour le code et les dépendances :
Intégration continue de la sécurité :
Intégrez des outils d’analyse de sécurité comme Bandit dans votre CI/CD pour effectuer des contrôles automatiques à chaque commit.
Revues de code :
Organisez des revues de code régulières où la sécurité est un sujet d’évaluation. Cela encourage les développeurs à écrire du code en gardant la sécurité à l’esprit.
Formation continue :
Restez informé des dernières pratiques de sécurisation du code en participant à des formations et en consultant régulièrement des ressources telles que le OWASP Top 10 pour les vulnérabilités les plus critiques.
En combinant ces techniques de sécurisation avancée avec les bases établies dans la première partie de l’article, vous renforcez significativement le niveau de sécurité de votre code Python.