Comment sécuriser et protéger son code Python ?

4.8/5 - (24 votes)

Les différentes méthodes de sécurisation

 

Il existe plusieurs moyens de sécuriser son code Python, voici quelques exemples :

 

  1. 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.
  2. É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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

 

bandit pour sécuriser son code python

 

 

bandit recherche de vulnérabilités python

 

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.

  1. Utiliser un environnement virtuel :Travailler dans un environnement virtuel, comme celui créé par venv ou virtualenv, 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.
  2. 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.
  3. Analyser les dépendances pour les vulnérabilités :Des outils comme Safety, Snyk ou PyUp scannent automatiquement vos fichiers requirements.txt ou Pipfile.lock à la recherche de dépendances connues pour leurs failles de sécurité.
  4. 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.