🎯 Objectif de la semaine
Maîtriser les structures de données avancées de Python pour organiser et manipuler efficacement des informations complexes en cybersécurité. Transformer des scripts simples en outils professionnels modulaires et réutilisables.
Transition critique : Passer du scripting basique à l'ingénierie d'outils. Un bon hacker sait écrire du code, un excellent hacker sait structurer son code.
Ordonnées, modifiables
index: 0, 1, 2...] B --> D[🗂️ Dictionnaires
Clé → Valeur
rapide accès] B --> E[🔗 Tuples
Immuables
périodes, coordonnées] B --> F[📦 Sets
Uniques, non-ordonnés
IPs uniques] C --> G[🔍 Analyse Séquentielle
Logs, scans, captures] D --> H[⚡ Recherche Rapide
Inventaire, mapping] E --> I[🔒 Données Constantes
Configurations, seuils] F --> J[🎯 Élimination Doublons
IPs uniques, ports] G --> K[🎨 JSON/CSV
Export/Import structuré] H --> K I --> K J --> K K --> L[🚀 Outils Professionnels
Modulaires, réutilisables] style C fill:#0a0e17,stroke:#00d4ff,stroke-width:2px style D fill:#11151f,stroke:#9d4edd,stroke-width:2px style L fill:#00f5a0,stroke:#000,stroke-width:2px
📚 Sujet – Théorie (1 heure)
Les structures de données : l'architecture invisible des outils
🏗️ Pourquoi la structure compte :
En cybersécurité, vous manipulez des données complexes :
- Centaines d'IPs avec différents statuts
- Milliers de logs avec timestamps et événements
- Dizaines de services avec versions et vulnérabilités
- Réseaux entiers avec topologies et relations
Choisir la bonne structure de données, c'est choisir le bon conteneur pour vos informations.
Les quatre piliers de Python en sécurité
| Structure | Syntaxe | Caractéristiques | Cas d'usage sécurité | Complexité accès |
|---|---|---|---|---|
| Liste | [] |
Ordonnée, mutable, doublons autorisés | Résultats de scan, logs séquentiels, commandes | O(n) recherche O(1) index |
| Dictionnaire | {} |
Clé → Valeur, non ordonné* | Inventaire IP→services, configs, mappings | O(1) recherche O(1) insertion |
| Tuple | () |
Ordonné, immuable, fixe | Coordonnées réseau, versions, constantes | O(1) index Immuable |
| Set | set() |
Non ordonné, unique, mutable | IPs uniques, ports ouverts uniques, tags | O(1) recherche O(1) insertion |
* Les dictionnaires Python 3.7+ conservent l'ordre d'insertion.
Fonctions : l'art de la modularité
⚠️ Les 3 principes des fonctions efficaces :
- Une fonction = une responsabilité (Single Responsibility Principle)
- Nom explicite :
scan_port()pasdo_stuff() - Documentation complète avec docstring (triple guillemets)
def analyze_port_scan(results, threshold=100):
"""
Analyse les résultats d'un scan de ports pour détecter des scans agressifs.
Args:
results (list): Liste de tuples (ip, port, status)
threshold (int): Seuil de ports scannés pour alerte
Returns:
dict: Résultats d'analyse avec IPs suspectes
"""
ip_counts = {}
for ip, port, status in results:
ip_counts[ip] = ip_counts.get(ip, 0) + 1
suspicious = {ip: count for ip, count in ip_counts.items()
if count > threshold}
return {
'total_scans': len(results),
'unique_ips': len(ip_counts),
'suspicious_ips': suspicious
}
JSON : le langage universel des données structurées
📋 Pourquoi JSON est essentiel :
- Interopérabilité : Lu par tous les langages et outils
- Lisibilité : Format texte facile à déboguer
- Hiérarchique : Supporte structures complexes imbriquées
- Standard : API REST, configurations, exports
En sécurité, vous utiliserez JSON pour :
- Exporter des résultats d'analyse
- Stocker des configurations d'outils
- Communiquer avec des APIs (Shodan, VirusTotal)
- Sauvegarder des inventaires réseau
Les compréhensions : la puissance Pythonique
Python offre une syntaxe concise pour transformer des données :
# Liste classique
results = []
for ip in ip_list:
if is_suspicious(ip):
results.append(ip)
# Liste en compréhension (plus Pythonique)
results = [ip for ip in ip_list if is_suspicious(ip)]
# Dictionnaire en compréhension
service_ports = {'ssh': 22, 'http': 80, 'https': 443}
inverted_ports = {port: service for service, port in service_ports.items()}
# Résultat: {22: 'ssh', 80: 'http', 443: 'https'}
💡 Philosophie du code structuré :
"Écrivez du code comme si la personne qui devra le maintenir était un psychopathe qui connaît votre adresse."
En sécurité, votre code doit être :
- Lisible : Les autres doivent pouvoir le comprendre rapidement
- Maintenable : Facile à modifier et étendre
- Robuste : Gère les cas d'erreur et données inattendues
- Documenté : Explique le "pourquoi" pas juste le "comment"
🔬 Mission pratique – Laboratoire (2 heures)
Scénario : Création d'un système d'inventaire réseau intelligent
Vous devez développer un outil pour cartographier un réseau, stocker les découvertes dans une structure organisée, et exporter les résultats pour analyse.
📝 Organisation professionnelle :
~/mon_lab/semaine5_structures/ ├── scripts/ # Code source ├── data/ # Données brutes ├── reports/ # Exports générés ├── tests/ # Tests unitaires └── README.md # Documentation
Étapes détaillées : Construction d'un outil complet
-
Configuration de l'environnement
# Structure du projet cd ~/mon_lab mkdir -p semaine5_structures/{scripts,data,reports,tests} cd semaine5_structures # Fichier de données d'exemple cat > data/scan_results.txt << 'EOF' 192.168.1.1:22:open:ssh:OpenSSH_7.9 192.168.1.1:80:open:http:Apache_2.4.41 192.168.1.10:22:closed:ssh:Unknown 192.168.1.10:443:open:https:nginx_1.18 192.168.1.15:22:open:ssh:OpenSSH_8.2 192.168.1.15:21:open:ftp:vsftpd_3.0 192.168.1.20:3389:open:rdp:Windows_RDP 192.168.1.25:22:open:ssh:OpenSSH_7.9 192.168.1.25:80:open:http:Apache_2.4.38 192.168.1.25:443:open:https:Apache_2.4.38 192.168.1.30:22:filtered:ssh:Unknown 192.168.1.100:445:open:smb:Samba_4.9 EOF -
Création du module d'analyse principal
#!/usr/bin/env python3 """ PCTAMALOU - Semaine 5 : Système d'inventaire réseau Module principal pour l'analyse et la structuration des données réseau """ import json import csv from collections import defaultdict, Counter from datetime import datetime import ipaddress # ============================================================================ # CLASSES DE DONNÉES # ============================================================================ class NetworkHost: """Représente un hôte réseau avec ses services""" def __init__(self, ip_address): self.ip = ip_address self.services = [] # Liste de services self.os_guess = None self.first_seen = datetime.now() self.last_seen = datetime.now() self.tags = set() # Tags pour classification def add_service(self, port, status, service, version): """Ajoute un service à l'hôte""" service_data = { 'port': port, 'status': status, 'service': service, 'version': version, 'discovered': datetime.now() } self.services.append(service_data) self.last_seen = datetime.now() def to_dict(self): """Convertit l'hôte en dictionnaire pour export""" return { 'ip': self.ip, 'services': self.services, 'os_guess': self.os_guess, 'first_seen': self.first_seen.isoformat(), 'last_seen': self.last_seen.isoformat(), 'tags': list(self.tags) } # ============================================================================ # FONCTIONS UTILITAIRES # ============================================================================ def parse_scan_file(filepath): """ Parse un fichier de résultats de scan Args: filepath (str): Chemin vers le fichier Returns: list: Liste d'objets NetworkHost """ hosts = {} # Dictionnaire IP -> NetworkHost try: with open(filepath, 'r') as f: for line_num, line in enumerate(f, 1): line = line.strip() if not line or line.startswith('#'): continue try: # Format: IP:PORT:STATUS:SERVICE:VERSION ip, port, status, service, version = line.split(':') port = int(port) # Création ou récupération de l'hôte if ip not in hosts: hosts[ip] = NetworkHost(ip) # Ajout du service hosts[ip].add_service(port, status, service, version) except ValueError as e: print(f"[WARN] Ligne {line_num} mal formatée: {line}") continue return list(hosts.values()) except FileNotFoundError: print(f"[ERREUR] Fichier {filepath} non trouvé") return [] except Exception as e: print(f"[ERREUR] Échec parsing: {e}") return [] def analyze_network(hosts): """ Analyse une liste d'hôtes pour en extraire des insights Args: hosts (list): Liste d'objets NetworkHost Returns: dict: Statistiques et insights """ if not hosts: return {} stats = { 'total_hosts': len(hosts), 'total_services': 0, 'services_by_type': Counter(), 'ports_by_status': defaultdict(int), 'common_versions': Counter(), 'ip_ranges': set() } for host in hosts: stats['total_services'] += len(host.services) # Analyse des services for service in host.services: stats['services_by_type'][service['service']] += 1 stats['ports_by_status'][service['status']] += 1 if service['version'] != 'Unknown': stats['common_versions'][service['version']] += 1 # Classification automatique classify_host(host) # Extraction du réseau try: network = ipaddress.ip_network(f"{host.ip}/24", strict=False) stats['ip_ranges'].add(str(network)) except: pass return stats def classify_host(host): """Classe un hôte en fonction de ses services""" services = {s['service'] for s in host.services} if 'ssh' in services: host.tags.add('linux_server') if 'rdp' in services or 'smb' in services: host.tags.add('windows_host') if 'http' in services or 'https' in services: host.tags.add('web_server') if 'ftp' in services: host.tags.add('file_server') # Hôte avec nombreux services = probable serveur if len(host.services) > 3: host.tags.add('multi_service') def export_to_json(hosts, output_path): """Exporte les hôtes au format JSON""" data = { 'generated': datetime.now().isoformat(), 'hosts': [host.to_dict() for host in hosts], 'metadata': { 'total_hosts': len(hosts), 'total_services': sum(len(h.services) for h in hosts) } } try: with open(output_path, 'w') as f: json.dump(data, f, indent=2, default=str) return True except Exception as e: print(f"[ERREUR] Export JSON échoué: {e}") return False def export_to_csv(hosts, output_path): """Exporte les services au format CSV""" try: with open(output_path, 'w', newline='') as f: writer = csv.writer(f) writer.writerow(['IP', 'Port', 'Status', 'Service', 'Version', 'Tags']) for host in hosts: for service in host.services: writer.writerow([ host.ip, service['port'], service['status'], service['service'], service['version'], ','.join(host.tags) ]) return True except Exception as e: print(f"[ERREUR] Export CSV échoué: {e}") return False # ============================================================================ # FONCTION PRINCIPALE # ============================================================================ def main(): print("[*] PCTAMALOU - Système d'inventaire réseau") print("[*] Analyse des données...") # 1. Parsing des données hosts = parse_scan_file('data/scan_results.txt') if not hosts: print("[!] Aucune donnée à analyser") return print(f"[+] {len(hosts)} hôtes chargés") # 2. Analyse stats = analyze_network(hosts) print(f"[+] {stats['total_services']} services découverts") # 3. Affichage des insights print("\n📊 INSIGHTS:") print(f" • Services par type: {dict(stats['services_by_type'])}") print(f" • Ports par statut: {dict(stats['ports_by_status'])}") if stats['common_versions']: print(f" • Versions courantes: {stats['common_versions'].most_common(3)}") # 4. Export print("\n💾 EXPORT DES DONNÉES:") if export_to_json(hosts, 'reports/inventory.json'): print(" ✅ JSON exporté: reports/inventory.json") if export_to_csv(hosts, 'reports/services.csv'): print(" ✅ CSV exporté: reports/services.csv") # 5. Recherche avancée (exemple) print("\n🔍 RECHERCHES AVANCÉES:") # Hôtes Linux avec SSH linux_hosts = [h for h in hosts if 'linux_server' in h.tags] print(f" • Hôtes Linux détectés: {len(linux_hosts)}") # Services potentiellement vulnérables vulnerable_services = [] for host in hosts: for service in host.services: if 'OpenSSH_7.' in service['version']: # Exemple de détection vulnerable_services.append(f"{host.ip}:{service['port']}") if vulnerable_services: print(f" ⚠️ Services potentiellement vulnérables: {vulnerable_services}") print("\n[+] Analyse terminée avec succès") # ============================================================================ # POINT D'ENTRÉE # ============================================================================ if __name__ == "__main__": main() -
Exécution et analyse des résultats
# Exécution cd ~/mon_lab/semaine5_structures python3 scripts/network_inventory.py # Inspection des exports cat reports/inventory.json | head -50 cat reports/services.csv # Analyse avec outils Python interactifs python3 -c " import json with open('reports/inventory.json') as f: data = json.load(f) print(f'Hôtes: {len(data[\"hosts\"])}') print(f'Services totaux: {data[\"metadata\"][\"total_services\"]}')" -
Exercices pratiques avancés
Exercice 1 : Ajoutez une fonction de recherche :
def find_hosts_by_service(hosts, service_name): """Trouve tous les hôtes exécutant un service spécifique""" return [host for host in hosts if any(s['service'] == service_name for s in host.services)] # Utilisation web_servers = find_hosts_by_service(hosts, 'http') print(f"Serveurs web: {[h.ip for h in web_servers]}")Exercice 2 : Implémentez un système de scoring de risque :
def calculate_risk_score(host): """Calcule un score de risque basé sur les services""" score = 0 risk_services = {'ftp': 2, 'telnet': 3, 'rdp': 2, 'smb': 2} for service in host.services: if service['status'] == 'open': score += risk_services.get(service['service'], 1) if 'Unknown' in [s['version'] for s in host.services]: score += 1 return score # Classement par risque hosts_sorted = sorted(hosts, key=calculate_risk_score, reverse=True) print("Hôtes les plus risqués:", [h.ip for h in hosts_sorted[:3]])
🎯 Défi de structuration avancé
Créez un système complet qui :
- Lit plusieurs fichiers de scan de formats différents (Nmap, Masscan)
- Normalise les données dans une structure unifiée
- Détecte les changements entre deux scans (nouveaux services, hôtes)
- Génère un rapport de différences avec priorités
- Exporte en HTML pour visualisation web
Indices : Utilisez des set() pour les comparaisons, difflib pour les différences textuelles, et un template HTML simple.
📋 Objectif de la semaine – Checklist de validation
✅ Compétences techniques acquises
- Maîtriser les opérations sur listes, dicts, sets, tuples
- Écrire des fonctions modulaires avec docstrings
- Exporter/importer des données en JSON/CSV
- Utiliser des compréhensions pour transformer des données
- Structurer un projet avec classes et modules
- Analyser des données réseau complexes
🧠 Changement mental accompli
- Penser en termes de structures de données, pas de variables isolées
- Comprendre les compromis performance/lisibilité/maintenabilité
- Voir le code comme un système, pas une collection de lignes
- Appliquer des principes d'architecture logicielle même dans des scripts
🏗️ Bonne pratique d'architecture :
Commencez toujours par modéliser vos données avant d'écrire du code. Posez-vous :
- Quelles sont mes entités principales ? (Hôtes, Services, Scans)
- Quelles sont leurs relations ? (Un hôte a plusieurs services)
- Quelles opérations vais-je faire souvent ? (Recherche, tri, filtre)
- Comment vais-je persister ces données ? (JSON, base de données)
📚 Ressources gratuites recommandées
Pour approfondir :
- Python Data Structures : Guide complet Real Python
- JSON en profondeur : Documentation officielle
- Design Patterns Python : Patterns pour code maintenable
⚠️ Anti-patterns à éviter
- Listes gigantesques : Pour 100k+ éléments, utilisez des générateurs
- Dictionnaires trop complexes : Au-delà de 3 niveaux, pensez classes
- Fonctions trop longues : Une fonction > 50 lignes doit être découpée
- Variables globales : Préférez le passage de paramètres
- JSON non validé : Toujours valider les données entrantes
🔮 Frère d'armes,
Tu viens de franchir une étape majeure dans ton apprentissage.
Avant cette semaine, tu écrivais du code. Maintenant, tu architectures des solutions.
La différence entre un script et un outil professionnel ne réside pas dans le nombre de lignes, mais dans :
- La structure des données
- La modularité du code
- La maintenabilité dans le temps
- La scalabilité face à plus de données
Ton système d'inventaire réseau n'est qu'un début. Les mêmes principes s'appliqueront à :
• L'analyse de logs de sécurité
• La gestion de vulnérabilités
• L'orchestration de scans
• La corrélation d'événements
• La création de dashboards
Tu as appris à penser comme un ingénieur, pas juste comme un scripteur.
Dans la semaine 6, nous quitterons le monde des scripts pour entrer dans celui des réseaux réels. Nous verrons comment les machines communiquent, comment capturer ce trafic, et comment l'analyser pour y détecter des anomalies.
Mais désormais, tu as une compétence fondamentale : tu sais organiser l'information. Et en cybersécurité, l'information organisée, c'est du pouvoir.
Pratique ces concepts. Restructure tes anciens scripts. Améliore leur architecture. Chaque refactoring te rendra meilleur.
Tu n'es plus un débutant. Tu es devenu un architecte.
— Platon-Y