Chemins autorisés pour les liens symboliques
Par défaut, OxPHP refuse toute requête qui se résout vers un chemin situé hors du DOCUMENT_ROOT canonique. Un lien symbolique à l'intérieur du DOCUMENT_ROOT pointant vers un répertoire externe renvoie 404 et la résolution de chemin journalise Blocked request: resolved path escapes document root.
C'est le bon comportement par défaut : il empêche la traversée de répertoires, les attaques TOCTOU par échange de lien symbolique et l'exposition accidentelle de fichiers de configuration ou de secrets situés un niveau au-dessus. Mais il bloque aussi des schémas légitimes que les frameworks utilisent depuis des années : le php artisan storage:link de Laravel, les bundles d'actifs de Symfony, les volumes de téléversement partagés montés dans plusieurs conteneurs.
SYMLINK_ALLOW_PATHS est l'activation explicite : vous y listez les chemins du système de fichiers vers lesquels les liens symboliques sous DOCUMENT_ROOT ont le droit de se résoudre. Tout ce qui ne figure pas dans la liste conserve le comportement strict du 404.
Configuration
# Absolute paths, comma-separated
SYMLINK_ALLOW_PATHS=/var/www/storage,/opt/shared/assets
# Relative paths resolve against DOCUMENT_ROOT
SYMLINK_ALLOW_PATHS=../storage,../shared/uploads
# Mixed
SYMLINK_ALLOW_PATHS=/opt/shared/cdn,../storage/app/publicLorsque la variable n'est pas définie (le cas par défaut), aucun lien symbolique ne peut quitter le DOCUMENT_ROOT.
Exemple Laravel
DOCUMENT_ROOT=/app/public
SYMLINK_ALLOW_PATHS=../storage/app/publicphp artisan storage:link crée alors public/storage -> ../storage/app/public à l'intérieur du projet. Les URL vers /storage/<file> se résolvent à travers le lien symbolique vers /app/storage/app/public/<file>, qui se canonicalise en un chemin autorisé par la liste d'autorisation. Aucune modification de code dans l'application.
Fonctionnement
Au démarrage, chaque entrée est résolue :
- Entrées absolues — vérifiées par rapport à la liste noire (voir ci-dessous), puis passées à
realpath(3)(c'est-à-direstd::fs::canonicalize). Sirealpathéchoue (la cible n'existe pas), le serveur refuse de démarrer. - Entrées relatives — jointes au
DOCUMENT_ROOTcanonique, puis passées àrealpath.
Les chemins canoniques résultants sont stockés comme liste d'autorisation. Les doublons sont dédupliqués silencieusement.
Au moment de la requête, la couche de routage canonicalise le chemin de fichier résolu et vérifie qu'il satisfait l'une des conditions suivantes :
- il se trouve à l'intérieur du
DOCUMENT_ROOT, ou - il correspond exactement à l'une des entrées de la liste d'autorisation (cibles de type fichier), ou
- il commence par l'une des entrées de la liste d'autorisation suivie d'un
/(cibles de type répertoire).
Le même contrôle s'exécute une seconde fois comme garde TOCTOU dans le chemin de service des fichiers statiques, après le cache de routes, avant tout appel système de lecture.
Liste noire
Un petit ensemble de chemins ne peut jamais figurer dans SYMLINK_ALLOW_PATHS ; des fautes de frappe et des malentendus élargiraient sinon considérablement la surface d'attaque. Le serveur refuse de démarrer si une entrée se résout vers un chemin de la liste noire.
Interdits en correspondance exacte :
/ /etc /proc /sys /dev /var /home /tmp /root /usr /srvInterdits en préfixe (l'entrée se trouve sous l'un de ces répertoires) :
/etc /proc /sys /dev /tmp /root /usr/var, /home et /srv sont en correspondance exacte uniquement : /srv seul est rejeté, mais /srv/myapp/storage est autorisé, tout comme /var/www/storage et /home/<any>/... le sont. Les entrées sont vérifiées deux fois : une fois par rapport au chemin brut fourni par l'administrateur (afin qu'un /etc -> /private/etc à la macOS ne puisse pas blanchir un chemin de la liste noire à travers realpath), une fois par rapport à la forme canonique (défense en profondeur contre les évasions de cible de lien symbolique).
La liste noire elle-même est codée en dur ; aucune variable d'environnement ne permet de l'étendre. Le comportement par défaut est le minimum conservateur qui attrape les erreurs de type faute de frappe ; les administrateurs qui ont besoin de politiques plus strictes doivent les superposer à l'extérieur (permissions du système de fichiers, restrictions de montage de conteneur, profils AppArmor/SELinux).
Modes de défaillance
| Erreur de configuration | Résultat |
|---|---|
| La cible de l'entrée n'existe pas sur le disque | Le serveur refuse de démarrer, l'erreur nomme l'entrée et canonicalize |
| L'entrée correspond à la liste noire (brute ou canonique) | Le serveur refuse de démarrer, l'erreur nomme l'entrée et la règle de la liste noire |
| Variable d'environnement vide ou composée uniquement d'espaces | Traitée comme non définie — comportement strict par défaut |
| Entrées en double | Dédupliquées silencieusement après canonicalisation |
| Aucun lien symbolique n'existe encore au démarrage | La liste d'autorisation est enregistrée mais reste inerte jusqu'à l'apparition d'un lien symbolique ; aucun contrôle de démarrage n'exige le lien symbolique |
Notes de sécurité
- La liste d'autorisation est en opt-in — le comportement par défaut sûr « aucune évasion » est préservé lorsque la variable n'est pas définie
- Les entrées sont canonicalisées au démarrage, de sorte que les
..et les liens symboliques intermédiaires dans le chemin de l'entrée sont réduits avant stockage - La canonicalisation à l'exécution ferme la fenêtre TOCTOU d'échange de lien symbolique — le chemin validé est le chemin qui est lu
- Les cibles de type fichier correspondent exactement ; les cibles de type répertoire correspondent par préfixe de répertoire. Lister un fichier à
/etc/passwdserait de toute façon rejeté par la liste noire, mais plus généralement : lister un fichier unique à/opt/shared/license.keyn'accorde pas implicitement l'accès à ses voisins - Les résultats de validation de chemin sont mis en cache par URL demandée (un
realpathpar URL unique jusqu'à éviction), de sorte que le coût à l'exécution est amorti
Voir aussi
- Chemins PHP refusés — bloque l'exécution de PHP sous des globs d'URI spécifiques ; orthogonal à la politique des liens symboliques
- Blocage des chemins pointés — refuse la traversée de type
.well-knownet les fuites de fichiers pointés - Proxys de confiance — frontière de confiance distincte pour les en-têtes
X-Forwarded-* - Référence de configuration — toutes les variables d'environnement