Skip to content

Fichiers de sauvegarde exposés : récupération de code source et de secrets

Un fichier de sauvegarde créé manuellement ou par un éditeur de texte dans la racine web devient une ressource HTTP comme une autre. Le serveur ne l’exécute pas — il le sert en texte brut. Le code source, les identifiants de base de données et les clés API deviennent lisibles directement dans le navigateur.

Quand un développeur modifie config.php en production, il crée souvent une copie avant d’intervenir :

Terminal window
cp config.php config.php.bak
# ou
cp config.php config.php.old

L’éditeur de texte fait la même chose automatiquement :

ÉditeurFichier de sauvegarde créé
Vimfichier.php~, .fichier.php.swp
Nanofichier.php~
Geditfichier.php~
Emacsfichier.php~, #fichier.php#

Ces fichiers restent sur le serveur après l’intervention. Le serveur web les sert sans les traiter : config.php exécute le PHP et renvoie une page HTML vide — config.php.bak retourne le code source PHP en clair.

index.php~
index.php.bak
index.php.old
index.php.save
index.php.orig
index.php.backup
index.php.copy
index.php.tmp
index.php.1
index.php_bak
.index.php.swp # fichier swap Vim
#index.php# # sauvegarde Emacs

Ces patterns s’appliquent à tous les fichiers sensibles : config.php, wp-config.php, .htpasswd, database.php, settings.py, application.properties.

Tester manuellement les extensions courantes

Section titled “Tester manuellement les extensions courantes”
Terminal window
# Tester les variantes sur un fichier cible
for ext in ~ .bak .old .save .orig .backup .copy .tmp .1 _bak; do
code=$(curl -s -o /dev/null -w "%{http_code}" "https://cible.fr/config.php${ext}")
echo "$code — config.php${ext}"
done

Un code 200 indique que le fichier est accessible. Un 403 signifie qu’il existe mais que l’accès est bloqué. Un 404 signifie qu’il n’existe pas.

Terminal window
# Utiliser une wordlist orientée fichiers de sauvegarde
gobuster dir \
-u https://cible.fr \
-w /usr/share/wordlists/dirb/common.txt \
-x bak,old,save,orig,backup,copy,tmp,~ \
-s 200,301
# Avec une wordlist de fichiers PHP courants
gobuster dir \
-u https://cible.fr \
-w /usr/share/seclists/Discovery/Web-Content/PHP.fuzz.txt \
-x bak,old,~

Les fichiers swap Vim ont un nommage spécifique : .nomfichier.ext.swp. Ils sont binaires mais contiennent le texte du fichier en clair dans leur structure.

Terminal window
curl -s https://cible.fr/.config.php.swp -o config.swp
# Récupérer le contenu texte du fichier swap
strings config.swp

Un fichier config.php.bak typique contient :

<?php
define('DB_HOST', 'localhost');
define('DB_NAME', 'production_db');
define('DB_USER', 'app_user');
define('DB_PASS', 'Sup3rS3cr3tP@ss');
define('API_KEY', 'sk-live-xxxxxxxxxxxxxxxxxxxxxxxxxxx');
define('JWT_SECRET', 'monSecretJWT2024');
?>

Un fichier settings.py.bak Django :

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'production',
'USER': 'django',
'PASSWORD': 'motDePasseProduction',
'HOST': '10.0.0.5',
}
}
SECRET_KEY = 'django-insecure-xxxxxxxxxxxxxxxxxxxxxxxxxxx'
AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE'
AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'

Bloquer les extensions de sauvegarde côté serveur

Section titled “Bloquer les extensions de sauvegarde côté serveur”

Apache (.htaccess ou httpd.conf) :

<FilesMatch "(\.bak|\.old|\.save|\.orig|\.backup|\.copy|\.tmp|~|\.swp|_bak)$">
Require all denied
</FilesMatch>

Nginx :

location ~* \.(bak|old|save|orig|backup|copy|tmp|swp)$|~$ {
deny all;
return 404;
}

IIS (web.config) :

<configuration>
<system.webServer>
<security>
<requestFiltering>
<denyUrlSequences>
<add sequence=".bak" />
<add sequence=".old" />
<add sequence=".save" />
<add sequence="~" />
</denyUrlSequences>
</requestFiltering>
</security>
</system.webServer>
</configuration>

La source du problème est l’édition directe de fichiers sur le serveur. Un pipeline de déploiement supprime cette pratique :

  1. Modifier le fichier localement
  2. Passer par git et une CI/CD
  3. Déployer via rsync, Ansible, ou un outil de déploiement

Aucun éditeur de texte n’ouvre de fichier sur le serveur → aucun fichier ~ ou .swp ne se crée.

Conventions de nommage pour les sauvegardes légitimes

Section titled “Conventions de nommage pour les sauvegardes légitimes”

Quand une sauvegarde est nécessaire, la stocker hors de la racine web :

Terminal window
# À éviter — dans la racine web
cp /var/www/html/config.php /var/www/html/config.php.bak
# Correct — hors de la racine web
cp /var/www/html/config.php /var/backups/config.php.$(date +%Y%m%d)

La racine web est typiquement /var/www/html/ — tout ce qui s’y trouve est potentiellement accessible via HTTP. Les sauvegardes vont ailleurs.

Identifier les fichiers de sauvegarde déjà présents dans la racine web :

Terminal window
find /var/www/html -name "*.bak" -o -name "*.old" -o -name "*.save" \
-o -name "*~" -o -name "*.swp" -o -name "*.orig" 2>/dev/null

Supprimer les occurrences trouvées et vérifier les règles de déploiement pour éviter qu’elles réapparaissent.

Le serveur sert les fichiers .bak et ~ sans les exécuter. Un fichier PHP dont le contenu est confidentiel devient lisible en clair dès qu’une copie avec une extension non reconnue existe dans la racine web.

Bloquer ces extensions au niveau du serveur et stocker les sauvegardes hors de la racine web sont les deux mesures à appliquer ensemble — l’une sans l’autre laisse une surface d’attaque ouverte.