Aller au contenu
Sécurité medium

Gouvernance du code source

20 min de lecture

Vous signez vos images Docker, vous scannez vos dépendances, vous générez des attestations SLSA. Mais qui peut modifier votre code source ? Si n’importe qui peut pousser directement sur main, toute votre sécurité en aval est contournable.

Ce guide explique comment verrouiller l’accès à votre code pour que chaque modification passe par une validation explicite avant d’atteindre la branche principale.

Pourquoi la gouvernance du code source est critique

Section intitulée « Pourquoi la gouvernance du code source est critique »

Sur GitHub et GitLab, sans configuration particulière :

  • Tout collaborateur peut pousser sur main — Un développeur (ou un compte compromis) peut modifier le code de production sans validation
  • Personne n’est obligé de valider — Même si quelqu’un ouvre une PR, elle peut être mergée sans approbation
  • Les workflows CI/CD sont modifiables — Un attaquant peut ajouter une étape qui exfiltre vos secrets vers un serveur externe
  • Pas de trace de qui a approuvé quoi — En cas d’incident, impossible de reconstituer la chaîne de décision

Un compte compromis ou un insider malveillant peut :

  1. Injecter une backdoor dans le code applicatif
  2. Modifier un workflow CI pour voler des secrets (tokens, clés API)
  3. Altérer le Dockerfile pour inclure un malware dans l’image
  4. Changer les dépendances vers des packages malveillants

Sans gouvernance, ces modifications passent directement en production.

La sécurisation du code source repose sur trois mécanismes qui fonctionnent ensemble :

MécanismeRôleSans lui
Branch ProtectionBloque les push directs, exige des PR validéesN’importe qui peut modifier main
CODEOWNERSDésigne qui doit approuver chaque zone du codeN’importe quel collaborateur peut approuver
CONTRIBUTING.mdDocumente les règles pour les contributeursRègles floues, erreurs de bonne foi

La protection de branche est le gardien automatique de votre dépôt. Elle empêche les modifications directes sur main et impose un processus de validation via Pull Request.

ProtectionCe qu’elle empêche
Require PR before mergingPush direct sur la branche protégée
Required approvalsMerge sans validation humaine
Require Code Owner reviewMerge sans l’accord des responsables désignés
Require status checksMerge avec une CI en échec
Block force pushesRéécriture de l’historique Git
Do not allow bypassingContournement par les admins
  1. Accéder aux paramètres de branche

    Dans votre dépôt, allez dans SettingsBranchesAdd branch ruleset (nouvelle interface) ou Add rule (ancienne interface).

  2. Nommer et cibler la règle

    • Ruleset name : Protect main branch
    • Enforcement status : Active
    • Target branches : Cliquez “Add target” → “Include by pattern” → main

    Vous pouvez ajouter d’autres branches : release/*, develop, etc.

  3. Activer les protections essentielles

    Dans la section “Rules”, cochez :

    • Require a pull request before merging

      • Required approvals : 1 (ou 2 pour les projets critiques)
      • ✅ Dismiss stale pull request approvals when new commits are pushed
      • ✅ Require review from Code Owners
    • Require status checks to pass before merging

      • ✅ Require branches to be up to date before merging
      • Ajoutez vos checks CI (tests, lint, build…)
    • Block force pushes

  4. Empêcher le contournement par les admins

    En haut de la page, dans “Bypass list”, assurez-vous que personne n’est listé, ou cochez Do not allow bypassing the above settings.

    Sans cette option, un admin peut merger sans respecter les règles — ce qui annule tout l’intérêt de la protection.

  5. Sauvegarder

    Cliquez Create pour activer la règle.

  1. Accéder aux paramètres de branche

    Dans votre projet, allez dans SettingsRepositoryProtected branches.

  2. Ajouter la protection sur main

    • Branch : main
    • Allowed to merge : Maintainers (ou un rôle spécifique)
    • Allowed to push and merge : No one
    • Allowed to force push : Non coché
  3. Activer l’approbation par Code Owners

    Cochez Require approval from code owners si vous avez un fichier CODEOWNERS.

  4. Configurer les règles de Merge Request

    Dans SettingsMerge requests :

    • Merge method : Merge commit ou Squash commits
    • Squash commits when merging : Selon votre préférence
    • Merge checks :
      • ✅ Pipelines must succeed
      • ✅ All discussions must be resolved
  5. Configurer les règles d’approbation

    Dans SettingsMerge requestsApproval rules :

    • Créez une règle “Default” avec Approvals required: 1
    • Ajoutez les utilisateurs ou groupes éligibles

Testez que vous ne pouvez plus pousser directement :

Fenêtre de terminal
# Tentative de push direct sur main
git checkout main
echo "# Test de sécurité" >> README.md
git add README.md
git commit -m "test: direct push should fail"
git push origin main

Résultat attendu :

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Changes must be made through a pull request.

Si le push réussit, vérifiez votre configuration Branch Protection.

Branch Protection exige “1 approbation”, mais par qui ? Par défaut, n’importe quel collaborateur avec accès en écriture peut approuver. Un attaquant avec deux comptes (ou un complice) pourrait s’auto-approuver.

CODEOWNERS résout ce problème en désignant explicitement qui doit valider chaque partie du code. GitHub et GitLab assignent automatiquement ces personnes comme reviewers sur les Pull Requests.

Quand quelqu’un ouvre une PR :

  1. GitHub/GitLab analyse les fichiers modifiés
  2. Il consulte le fichier CODEOWNERS pour trouver les responsables
  3. Il assigne automatiquement ces personnes comme reviewers
  4. Si “Require Code Owner review” est activé, leur approbation est obligatoire

Analogie : c’est comme avoir des chefs de département. Le responsable sécurité valide les changements de sécurité, le responsable ops valide l’infrastructure. Personne ne peut modifier seul un domaine critique.

Créez le fichier .github/CODEOWNERS (GitHub) ou CODEOWNERS à la racine (GitLab) :

.github/CODEOWNERS
# ============================================================
# CODEOWNERS — Définit les responsables par zone de code
# ============================================================
# Chaque modification dans une zone listée nécessite l'approbation
# d'au moins un CODEOWNER de cette zone.
#
# IMPORTANT : la dernière règle qui matche un fichier gagne.
# Mettez les règles générales en premier, les spécifiques après.
# ============================================================
# --- Propriétaire global (fallback) ---
# S'applique à tous les fichiers qui ne matchent pas une règle plus spécifique
* @votre-username
# --- Workflows CI/CD (CRITIQUE) ---
# Les workflows peuvent accéder aux secrets et exfiltrer des données.
# Exigez une revue par l'équipe sécurité pour toute modification.
.github/workflows/ @votre-username @security-team
.github/actions/ @votre-username @security-team
.gitlab-ci.yml @votre-username @security-team
# --- Configuration infrastructure ---
# Ces fichiers définissent comment l'application est déployée.
# Un Dockerfile malveillant peut compromettre toute l'image.
Dockerfile @votre-username @ops-team
docker-compose*.yml @votre-username @ops-team
helm/ @votre-username @ops-team
k8s/ @votre-username @ops-team
terraform/ @votre-username @ops-team
# --- Dépendances (risque supply chain) ---
# Les fichiers de dépendances sont un vecteur d'attaque majeur.
# Dependency confusion, typosquatting, packages compromis...
package.json @votre-username @security-team
package-lock.json @votre-username @security-team
requirements.txt @votre-username @security-team
requirements*.txt @votre-username @security-team
Pipfile @votre-username @security-team
Pipfile.lock @votre-username @security-team
go.mod @votre-username @security-team
go.sum @votre-username @security-team
Cargo.toml @votre-username @security-team
Cargo.lock @votre-username @security-team
# --- Code applicatif ---
# Le code source principal, sous la responsabilité du tech lead.
src/ @votre-username @tech-lead
app/ @votre-username @tech-lead
lib/ @votre-username @tech-lead
# --- Tests ---
tests/ @votre-username @qa-lead
test/ @votre-username @qa-lead
spec/ @votre-username @qa-lead
# --- Documentation ---
docs/ @votre-username
*.md @votre-username
PatternSignificationExemple
*Tous les fichiers (fallback)* @admin
*.jsTous les fichiers avec cette extension*.py @python-team
/docs/Dossier docs/ à la racine uniquement/src/ @core-team
docs/Tout dossier docs/ n’importe oùtests/ @qa
@userUtilisateur GitHub/GitLab@alice
@org/teamÉquipe dans une organisation@acme/security

Bon maintenant, testons que CODEOWNERS assigne bien les reviewers automatiquement.

  1. Créez une branche de test

    Fenêtre de terminal
    git checkout -b test/codeowners
  2. Modifiez un fichier dans une zone protégée

    Fenêtre de terminal
    echo "# Test" >> .github/workflows/ci.yml
    git add .github/workflows/ci.yml
    git commit -m "test: verify CODEOWNERS"
    git push origin test/codeowners
  3. Ouvrez une Pull Request

    Dans GitHub/GitLab, créez une PR de test/codeowners vers main.

  4. Vérifiez les reviewers assignés

    Les CODEOWNERS de .github/workflows/ doivent apparaître automatiquement comme reviewers requis.

  5. Testez le blocage

    Essayez de merger sans leur approbation — GitHub doit afficher “Review required from code owners”.

Étape 3 : Documenter les règles (CONTRIBUTING.md)

Section intitulée « Étape 3 : Documenter les règles (CONTRIBUTING.md) »

CODEOWNERS définit qui approuve. CONTRIBUTING.md explique comment contribuer correctement. C’est le mode d’emploi pour les contributeurs.

Sans documentation claire :

  • Les contributeurs font des erreurs de bonne foi (mauvais format de commit, PR mal structurée)
  • Les reviewers passent du temps à expliquer les mêmes règles
  • En cas d’incident, vous ne pouvez pas prouver que les règles existaient

Un CONTRIBUTING.md bien rédigé :

  • Réduit les frictions pour les nouveaux contributeurs
  • Accélère les revues de code
  • Sert de preuve d’audit que les processus étaient documentés

Créez CONTRIBUTING.md à la racine de votre dépôt :

CONTRIBUTING.md
# Guide de contribution
Ce projet applique des règles strictes pour garantir la qualité et la sécurité
du code. Toute contribution doit respecter ce guide.
## Processus de contribution
### 1. Créer une branche
Créez une branche depuis `main` avec un nom descriptif :
- `feature/description-courte` — Nouvelles fonctionnalités
- `fix/description-courte` — Corrections de bugs
- `security/description-courte` — Correctifs de sécurité
- `docs/description-courte` — Documentation
- `refactor/description-courte` — Refactoring sans changement fonctionnel
```bash
git checkout main
git pull origin main
git checkout -b feature/add-user-authentication
```
### 2. Faire vos modifications
- Commitez régulièrement avec des messages clairs
- Suivez les conventions de code du projet
- Ajoutez des tests pour les nouvelles fonctionnalités
### 3. Ouvrir une Pull Request
- **Titre** : Description concise du changement
- **Description** : Expliquez le "pourquoi", pas juste le "quoi"
- **Issues liées** : Utilisez `Fixes #123` ou `Closes #456`
- **Tests** : Indiquez comment tester votre modification
### 4. Processus de revue
- Au minimum **1 approbation** est requise pour merger
- Les zones sensibles (voir ci-dessous) nécessitent l'approbation des
**CODEOWNERS** désignés
- La CI doit être **verte** (tous les checks passent)
- Répondez aux commentaires des reviewers
## Zones sensibles
Ces fichiers/dossiers ont un impact critique sur la sécurité ou le
fonctionnement du projet. Ils nécessitent une attention particulière et
l'approbation d'un responsable spécifique.
| Zone | Risque | CODEOWNER |
|------|--------|-----------|
| `.github/workflows/` | Accès aux secrets CI/CD | @security-team |
| `Dockerfile` | Image de production compromise | @ops-team |
| `requirements*.txt` | Attaque supply chain | @security-team |
| `package*.json` | Attaque supply chain | @security-team |
| `helm/`, `k8s/` | Configuration de déploiement | @ops-team |
## Ce qui est interdit
- ❌ Push direct sur `main` — Toujours passer par une PR
- ❌ Merge sans approbation des CODEOWNERS requis
- ❌ Désactivation des checks CI pour forcer un merge
- ❌ Force push sur les branches protégées
- ❌ Merge avec des tests en échec
## Conventions de commit
Nous utilisons [Conventional Commits](https://www.conventionalcommits.org/) :
```
type(scope): description courte
Corps optionnel avec plus de détails.
Fixes #123
```
Types autorisés :
- `feat` : Nouvelle fonctionnalité
- `fix` : Correction de bug
- `docs` : Documentation
- `style` : Formatage (pas de changement de code)
- `refactor` : Refactoring
- `test` : Ajout/modification de tests
- `chore` : Maintenance, dépendances
## Signaler une vulnérabilité
**Ne créez pas d'issue publique pour les failles de sécurité.**
- Utilisez [GitHub Security Advisory](../../security/advisories/new)
- Ou contactez security@votre-domaine.fr
Nous nous engageons à :
- Accuser réception sous 48h
- Fournir une estimation du délai de correction sous 7 jours
- Vous créditer (si vous le souhaitez) dans les notes de version

GitHub reconnaît automatiquement ces emplacements :

  1. CONTRIBUTING.md (racine) — Recommandé
  2. .github/CONTRIBUTING.md
  3. docs/CONTRIBUTING.md

Placez-le à la racine pour une visibilité maximale.

  1. Activer Branch Protection (10 min)

    • PR obligatoire pour merger sur main
    • Au moins 1 approbation requise
    • Require Code Owner review activé
    • Status checks (CI) obligatoires
    • Block force pushes
    • Do not allow bypassing (même pour les admins)
  2. Créer le fichier CODEOWNERS (10 min)

    • Définir un owner par défaut (* @vous)
    • Protéger les zones critiques : workflows, Dockerfile, dépendances
    • Assigner les équipes appropriées
  3. Créer CONTRIBUTING.md (10 min)

    • Documenter le processus de contribution
    • Lister les zones sensibles et leurs responsables
    • Expliquer ce qui est interdit

Configuration minimale (recommandée pour tout projet)

Section intitulée « Configuration minimale (recommandée pour tout projet) »
  • Branch Protection activée sur main
  • Pull Request obligatoire pour merger
  • Au moins 1 approbation requise
  • Status checks (CI) obligatoires
  • Force push bloqué
  • Tout ci-dessus +
  • Fichier CODEOWNERS présent et actif
  • “Require review from Code Owners” activé
  • CONTRIBUTING.md documenté
  • “Do not allow bypassing” activé
  • Tout ci-dessus +
  • 2 approbations de personnes différentes
  • Commits signés obligatoires (GPG ou Gitsign)
  • SECURITY.md pour signaler les vulnérabilités
  • Audit log des modifications activé
ErreurConséquenceSolution
CODEOWNERS sans Branch ProtectionLes règles CODEOWNERS sont ignorablesActiver “Require review from Code Owners” dans Branch Protection
Admins exemptés des règlesLes admins peuvent contourner toutes les protectionsCocher “Do not allow bypassing the above settings”
Pattern * en dernierÉcrase toutes les règles spécifiquesMettre * en premier, règles spécifiques après
Pas de fallback ownerCertains fichiers n’ont pas de reviewerAjouter * @default-owner comme première règle
CONTRIBUTING trop longPersonne ne le litGarder l’essentiel, lier vers une doc détaillée si besoin
Équipe CODEOWNER inexistanteErreur silencieuse, revue non requiseVérifier que les équipes @org/team existent

SLSA (Supply chain Levels for Software Artifacts) définit des niveaux de maturité pour la sécurité du code source :

NiveauExigencesAttaques bloquées
L1Code versionné (Git)Perte de traçabilité
L2+ Branch Protection + Historique immuablePush direct, force push
L3+ CODEOWNERS + 1 revue obligatoireInsider seul, compte compromis
L4+ 2 revues de personnes différentesCollusion, attaque coordonnée

La configuration décrite dans ce guide vous amène au niveau Source L3 — suffisant pour la grande majorité des projets.

Pour atteindre L4 (haute sécurité), vous devez :

  • Exiger 2 approbations de personnes différentes
  • Activer les commits signés obligatoires
  • Implémenter un audit trail complet