2017-01-13 1 views
1

J'écris un simple serveur de fichiers en Python. Le nom de fichier est fourni par le client et doit être considéré comme non fiable. Comment vérifier qu'il correspond à un fichier dans le répertoire courant (dedans ou dans l'un de ses sous-répertoires)? Est-ce que quelque chose comme:Comment autoriser uniquement l'ouverture de fichiers dans le répertoire courant dans Python3?

pwd=os.getcwd() 
if os.path.commonpath((pwd,os.path.abspath(filename))) == pwd: 
    open(filename,'rb') 

suffit?

+0

qu'en est-il de https://docs.python.org/3/library/pathlib.html#pathlib.Path.resolve? –

Répondre

2

Convertir le nom de fichier à un chemin canonique en utilisant os.path.realpath, obtenir la partie du répertoire, et voir si le répertoire courant (sous forme canonique) est un préfixe de cette:

import os, os.path 

def in_cwd(fname): 
    path = os.path.dirname(os.path.realpath(fname)) 
    return path.startswith(os.getcwd()) 

En convertissant fname à un chemin canonique nous traitons les liens symboliques et les chemins contenant ../.


Mise à jour

Malheureusement, le code ci-dessus a un petit problème. Par exemple,

'/a/b/cd'.startswith('/a/b/c') 

retours True, mais nous avons certainement ne voulons que le comportement ici! Heureusement, il y a une solution facile: il suffit d'ajouter os.sep aux chemins avant d'effectuer le test de préfixe. La nouvelle version gère également tous les problèmes d'insensibilité à la casse du système d'exploitation via os.path.normcase.

import os, os.path 

def clean_dirname(dname): 
    dname = os.path.normcase(dname) 
    return os.path.join(dname, '') 

def in_cwd(fname): 
    cwd = clean_dirname(os.getcwd()) 
    path = os.path.dirname(os.path.realpath(fname)) 
    path = clean_dirname(path) 
    return path.startswith(cwd) 

Merci à DSM pour signalé la faille dans le code précédent.


Voici une version un peu plus efficace. Il utilise os.path.commonpath, ce qui est plus robuste que l'ajout de os.sep et de faire un test de préfixe de chaîne.

def in_cwd(fname): 
    cwd = os.path.normcase(os.getcwd()) 
    path = os.path.normcase(os.path.dirname(os.path.realpath(fname))) 
    return os.path.commonpath((path, cwd)) == cwd 
+0

C'est une très bonne solution. J'ai essayé de l'exécuter avec mes cas de test et cela semble échouer pour le cas de fichier inexistant, par exemple (par exemple 'foo.py'). Comme dans cela retourne vrai au lieu de faux. Edit: mais cela n'a pas d'importance. OP semble heureux de laisser potentiellement recevoir (et jeter des exceptions) lors de la réception de fichiers inexistants. Donc +1. – Tagc

+0

@Tagc Je viens de mettre à jour ma réponse, j'ai donc besoin de mettre à jour mon commentaire précédent aussi. :) Si 'in_cwd' renvoie' True', vous pouvez alors tester si le fichier existe réellement si vous voulez servir le fichier. Il y a un avantage à avoir 'in_cwd' renvoyant' True' sur des chemins valides pour des fichiers qui n'existent pas encore: vous pouvez l'utiliser pour valider un chemin proposé pour un fichier enregistré dans cwd. –