Django ImageField / FileField пользовательские функции upload_to и безопасность - PullRequest
3 голосов
/ 05 октября 2009

У меня есть часть модели, определенной так:

logo_image = models.ImageField(upload_to=lambda i, fn: "logo_%s"%(fn), height_field="logo_image_height", width_field="logo_image_width")

и задал вопрос о функции upload_to.

Согласно документации django для FileField.upload_to , вторым параметром, filename, является «Имя файла, которое изначально было задано файлу».

Теперь, зная о HTTP, загрузке файлов и т. Д., Клиент конечного пользователя может легко подделать имя файла. В частности, не мог ли конечный клиент загрузить файл с именем "/ etc / passwd", например, а затем, если я использую свой простой код (lambda i, fn: "logo_%s"%(fn)), не будет ли полученный файл загружен в /etc/passwd? Нужно ли экранировать параметр filename?

#using django's example of using full paths in settings module,
#MEDIA_ROOT="/tmp/media"
>>> os.path.join("/tmp/media/", "apple.jpg")
'/tmp/media/apple.jpg'
>>> os.path.join("/tmp/media/", "/etc/passwd")
'/etc/passwd'

Спасибо за любые предложения / ответы / разъяснения.

Редактировать

Важными методами для просмотра являются в файле files.py, рядом со строкой 272 :

272         def get_directory_name(self):
273             return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to))))
274     
275         def get_filename(self, filename):
276             return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename)))
277     
278         def generate_filename(self, instance, filename):
279             return os.path.join(self.get_directory_name(), self.get_filename(filename))

Определение пользовательского upload_to заменяет generate_filename (), как видно здесь :

226             if callable(upload_to):
227                 self.generate_filename = upload_to

Затем в методе save () :

89      def save(self, name, content, save=True):
90          name = self.field.generate_filename(self.instance, name)
91          self.name = self.storage.save(name, content)

И возвращенное имя файла передается в класс хранения, который в конечном итоге вызывает функцию замены django в утилитном модуле _os.py safe_join .

Эта функция облегчает мои страхи:

24    def safe_join(base, *paths):
25      """
26      Joins one or more path components to the base path component intelligently.
27      Returns a normalized, absolute version of the final path.
28  
29      The final path must be located inside of the base path component (otherwise
30      a ValueError is raised).
31      """

1 Ответ

1 голос
/ 31 августа 2010

Я думаю, что вы ответили на свой вопрос. Следует пояснить, что способ работы os.path.join () состоит в удалении предыдущих каталогов (в соответствии с документами Python, относящимися к os.path). Таким образом, поведение, которое вы наблюдали при вызове os.path.join (), согласуется с тем, как оно описано.

Еще одна вещь, на которую следует обратить внимание: функция get_filename () вызывает os.path.basename (), которая удаляет любые пути к каталогам и возвращает только базовое имя. Таким образом, без параметра upload_to = опасность этой возможности отсутствует.

Однако, если вы переопределите ImageField () своей собственной функцией upload_to, эта функция не будет вызываться, и может быть лучше вызвать os.path.basename (). Во-первых, это также позволит избежать сохранения имени файла как полного пути к каталогу. Поэтому я считаю, что предпочтительно также вызывать os.path.basename () в моей функции upload_to. Кто-нибудь еще поддерживал эту проблему?

Подробнее см .: http://hustoknow.blogspot.com/2010/08/try-me-out.html

...