Un SaaS mal gouverné ne tombe pas forcément à cause d’une faille spectaculaire. Il tombe souvent parce qu’un compte prestataire traîne encore, qu’un rôle administrateur a été donné trop largement, qu’un export contient des données sensibles ou qu’une intégration historique garde un token puissant. Dans ce tutoriel, on construit un mini-audit local avec des exports CSV et Python pour repérer ces signaux faibles avant qu’ils ne deviennent un incident.
L’objectif n’est pas de remplacer un CASB, un SIEM ou un outil IAM. L’objectif est plus pragmatique : donner à une petite équipe une méthode réutilisable pour auditer rapidement une plateforme SaaS critique, qu’il s’agisse de ServiceNow, Jira, GitHub Enterprise, Salesforce, Notion, Google Workspace ou Microsoft 365. Tant que vous pouvez exporter utilisateurs, rôles et journaux d’accès, la méthode fonctionne.
Préparer les trois exports nécessaires
Créez un dossier de travail, puis placez-y trois fichiers. Les noms peuvent changer, mais gardez une structure stable pour automatiser l’analyse.
mkdir -p ~/audit-saas
cd ~/audit-saas
touch users.csv roles.csv access_logs.csv
Le fichier users.csv doit contenir au minimum user,email,status,last_login. Le fichier roles.csv doit contenir user,role. Le fichier access_logs.csv doit contenir timestamp,user,ip,action,resource. Si votre SaaS exporte plus de champs, gardez-les : ils serviront plus tard.
Étape 1 : normaliser les dates et les identifiants
Les exports SaaS sont rarement parfaits. Avant de chercher des anomalies, on normalise les emails, les dates et les statuts. Créez audit_saas.py :
#!/usr/bin/env python3
import csv
from datetime import datetime, timezone, timedelta
from pathlib import Path
BASE = Path.home() / "audit-saas"
TODAY = datetime.now(timezone.utc)
def parse_date(value):
if not value:
return None
value = value.replace("Z", "+00:00")
try:
return datetime.fromisoformat(value)
except ValueError:
return datetime.strptime(value[:10], "%Y-%m-%d").replace(tzinfo=timezone.utc)
def read_csv(name):
with (BASE / name).open(newline="", encoding="utf-8") as handle:
return list(csv.DictReader(handle))
users = read_csv("users.csv")
roles = read_csv("roles.csv")
logs = read_csv("access_logs.csv")
for user in users:
user["email"] = (user.get("email") or user.get("user") or "").lower().strip()
user["last_login_dt"] = parse_date(user.get("last_login", ""))
print(f"{len(users)} utilisateurs, {len(roles)} rôles, {len(logs)} logs chargés")
Lancez-le une première fois. Si le script casse sur une date, corrigez le mapping avant d’aller plus loin. Un audit fiable commence par des données lisibles.
Étape 2 : détecter les comptes dormants
Un compte actif qui ne s’est pas connecté depuis 90 jours est rarement urgent seul, mais il devient dangereux s’il possède des droits élevés. Ajoutez ce bloc au script :
inactive_limit = TODAY - timedelta(days=90)
active_status = {"active", "enabled", "1", "true"}
dormant = []
for user in users:
status = (user.get("status") or "").lower()
last_login = user["last_login_dt"]
if status in active_status and (last_login is None or last_login < inactive_limit):
dormant.append(user["email"])
print("nComptes dormants actifs:")
for email in dormant[:100]:
print("-", email)
La sortie doit être relue avec le métier. Certains comptes techniques ne se connectent jamais comme un utilisateur normal, mais ils doivent alors être identifiés, documentés et limités.
Étape 3 : trouver les privilèges larges
Chaque SaaS utilise ses propres noms de rôles, mais quelques motifs reviennent : admin, owner, security, integration, api, superuser. Ajoutez une détection simple :
danger_words = ("admin", "owner", "super", "security", "api", "integration")
privileged = {}
for row in roles:
email = (row.get("user") or row.get("email") or "").lower().strip()
role = (row.get("role") or "").lower().strip()
if any(word in role for word in danger_words):
privileged.setdefault(email, set()).add(role)
print("nComptes privilégiés:")
for email, role_set in sorted(privileged.items()):
print("-", email, "=>", ", ".join(sorted(role_set)))
Le but n’est pas de supprimer tous les administrateurs. Le but est de vérifier qu’ils sont encore nécessaires, rattachés à une personne identifiable, protégés par MFA et exclus des usages quotidiens non administratifs.
Étape 4 : croiser dormance et privilèges
Le vrai signal apparaît quand on combine les deux listes. Un compte dormant sans droit fort est à nettoyer. Un compte dormant avec droit fort est une priorité.
critical = sorted(set(dormant) & set(privileged.keys()))
print("nPriorité haute : comptes dormants privilégiés")
for email in critical:
print("-", email, "=>", ", ".join(sorted(privileged[email])))
Dans une organisation mature, cette liste doit être vide. Si elle ne l’est pas, commencez par désactiver ou réduire les droits après validation du propriétaire applicatif.
Étape 5 : repérer les accès anonymes ou inattendus
Certains SaaS exposent des portails publics, des pages de support ou des formulaires anonymes. Ce n’est pas forcément un problème. En revanche, un accès anonyme à une ressource interne ou à une table sensible doit être expliqué.
sensitive = ("user", "group", "incident", "ticket", "attachment", "invoice", "customer")
anonymous_names = {"", "guest", "anonymous", "public"}
findings = []
for row in logs:
actor = (row.get("user") or "").lower().strip()
resource = (row.get("resource") or "").lower().strip()
action = (row.get("action") or "").lower().strip()
if actor in anonymous_names and any(word in resource for word in sensitive):
findings.append((row.get("timestamp"), actor or "anonymous", action, resource))
print("nAccès anonymes à vérifier:")
for item in findings[:50]:
print("-", item)
Cette règle est volontairement simple. Elle produit parfois des faux positifs, mais elle force une discussion utile : quelles ressources sont publiques par design, lesquelles le sont par accident ?
Étape 6 : produire un rapport court
Un audit ne vaut que s’il se transforme en actions. Ajoutez une sortie Markdown facile à partager dans un ticket interne.
report = BASE / "rapport-audit-saas.md"
report.write_text(f"""# Audit SaaS rapide
- Utilisateurs: {len(users)}
- Comptes dormants actifs: {len(dormant)}
- Comptes privilégiés: {len(privileged)}
- Dormants privilégiés: {len critical}
- Accès anonymes suspects: {len(findings)}
## Priorités
{chr(10).join("- " + email for email in critical) or "- Aucune priorité haute détectée"}
""", encoding="utf-8")
print("nRapport:", report)
Le rapport doit rester court. Une page de cinquante lignes sera lue, discutée et transformée en tickets. Un export brut de plusieurs milliers de lignes finira dans un dossier partagé que personne n’ouvrira. Pour chaque priorité haute, ajoutez ensuite une décision : désactiver, réduire les droits, conserver avec justification ou transférer au propriétaire métier.
Ce qu’il faut décider après l’audit
La sortie du script doit alimenter quatre décisions : désactivation des comptes inutiles, réduction des rôles trop larges, justification des accès publics et revue des intégrations. Pour chaque exception conservée, notez un propriétaire, une raison et une date de révision. Sans date, une exception devient permanente.
Je recommande aussi de séparer les comptes humains des comptes techniques. Un compte humain doit être rattaché à une personne, à un contrat et à un processus de départ. Un compte technique doit avoir une finalité documentée, un secret renouvelable, des droits minimaux et une rotation planifiée. Quand ces deux familles se mélangent, les audits deviennent confus et les incidents plus difficiles à comprendre.
Automatiser sans perdre le contrôle
Une fois les règles stabilisées, vous pouvez lancer le script chaque semaine et comparer les rapports. La première automatisation utile n’est pas l’envoi d’un long email, mais la détection des nouveautés : nouveau compte administrateur, nouveau rôle sensible, compte désactivé redevenu actif, ressource sensible consultée anonymement. C’est le delta qui intéresse l’équipe, pas la répétition du même inventaire.
# Exemple simple : conserver les rapports datés
DATE=$(date +%F)
python3 audit_saas.py
cp ~/audit-saas/rapport-audit-saas.md ~/audit-saas/archives/rapport-$DATE.md
Limites et bonnes pratiques
Ne mettez jamais d’exports contenant des données sensibles dans un dépôt Git. Travaillez dans un dossier local chiffré si possible, supprimez les fichiers après traitement et masquez les emails si le rapport circule largement. Le script doit aider à réduire le risque, pas créer une nouvelle fuite.
Enfin, automatisez seulement après deux ou trois audits manuels. Les premières exécutions servent à comprendre les formats, les faux positifs et les règles métier. Ensuite, vous pourrez planifier le script chaque semaine et envoyer uniquement les différences.
Conclusion
La sécurité SaaS commence rarement par un grand projet. Elle commence par des questions simples : qui a accès, depuis quand, à quoi, avec quel niveau de privilège ? Avec trois exports CSV et 80 lignes de Python, une équipe peut déjà trouver des comptes oubliés, des rôles excessifs et des accès publics douteux. Ce n’est pas glamour, mais c’est exactement le type d’hygiène qui réduit l’impact des prochaines failles SaaS.
Commentaires (0)
Laisser un commentaire
Les commentaires sont modérés. Questions WordPress, cybersécurité ou dev web bienvenues.