Функция просмотра не вызывается в Django

#python #django

#python #django

Вопрос:

У меня довольно очевидный недостаток безопасности на моем веб-сайте — пользователи могут вставить путь к файлу в адресную строку и загрузить его. Я бы предпочел, чтобы этого не произошло.

Проблема в том, что, похоже, она не вызывается. Пользователь может загружать любые файлы на сервере, независимо от того, соответствует ли pk request.user.id или нет.

Это соответствующая часть моего project/urls.py

 urlpatterns = patterns('',

    url(r'^admin/', include(admin.site.urls)),

    #security
    url(r'^media/uploads/(?P<pk>[^/] )', 'notendur.views.permit'),
    url(r'^media', 'notendur.views.permit'),
)
  

Это функция просмотра, которая должна проверять, является ли пользователь владельцем запрошенного файла. Я включаю все это, чтобы предоставить вам как можно больше информации.

Код над хэштегами очищает запрошенный адрес. Я знаю, что функция не вызывается, потому что я заменил sendfile() функцию на простую render('forbidden.html') , и ничего не произошло.

Как вы можете видеть, функция проверяет <pk> , совпадает ли ссылка с request.user.id , и обслуживает файл, если это так.

 def permit(request, pk):
    path = request.path
    path_list = path.split("/")
    s = ""
    for dir in path_list:
        if dir != "media" and path_list.index(dir) > path_list.index("media"):
            s  = dir   "/"
    s = s[:-1]  
    # The for loop would add a "/" to the filename. 
    # The system would think the file was a directory, and not a file.

    system_path = settings.MEDIA_ROOT   s

    if int(request.user.id) == int(pk) and int(request.user.id) >= 1:
        return sendfile(request, system_path)
    else:
        return render_to_response('forbidden.html')
    return HttpResponseRedirect('/notendur/list')
  

Одно очень интересное наблюдение, которое я должен отметить, заключается в том, что, когда я удалил оператор bottom return, localhost он больше не будет ссылаться на localhost/notendur/list .



Новое models.py:

 class Document(models.Model):
    filename = models.CharField(max_length=255, blank=False, null=False, default="")
    user = models.ForeignKey(User, related_name='files', null=False)

    docfile = models.FileField(upload_to=_upload_path)
    user_id = user.primary_key
    options = models.IntegerField(default=0)
    name = models.TextField(default=0)

    def get_upload_path(self,filename):
        return "uploads/" str(self.user.id)   '/'   str(date.today())   '/'   filename
  

Мой взгляд

 View:

    @login_required
    def file(request, filename):
        file = get_object_or_404(File, user=request.user, filename=filename)
        return HttpResponse(open(file.filename, 'rb').read())
  

urls.py:

     url(r'^file/(?P<filename>.*)$', 'file', name="file"),
  

Комментарии:

1. Вы пробовали размещать отпечатки везде, чтобы точно узнать, где он умирает?

2. Я заменил sendfile() на render(‘forbidden.html ‘), так что если функция вообще вызывается, она должна возвращать if или else, верно? Я все же попробую. Как мне увидеть вывод инструкций печати из функций представления Django?

3. Вы используете производственный сервер или сервер разработчика? Разве вы не переопределили /media/ url в nginx?

4. Я использую только Apache2

5. @Gudmundur Чтобы увидеть результат печати, вы просто смотрите на вывод в строке cmd, из которой вы его запускаете

Ответ №1:

Весь ваш подход ошибочен. Проверка request.user.id соответствия pk переданному в URL-адресе просто означает, что идентификатор вошедшего в систему пользователя должен соответствовать идентификатору, который он ввел в URL.

Это не подтверждает, что этот пользователь на самом деле является «владельцем» файла.

Если пользователь A (id = 1) владеет foo.txt , а пользователь B (id = 2) владеет bar.txt , ничто не мешает пользователю A получить доступ bar.txt по следующему URL:

 /media/bar.txt/2/
  

Этот код также особенно небезопасен:

 system_path = settings.MEDIA_ROOT   s
  

Поскольку ничто не мешает мне получить доступ к любому файлу, к которому имеет доступ ваш сервер:

 /media/../settings.py/1/
  

Наконец, использование /media/ каталога не будет работать, потому что оно вообще не обслуживается через представление django (кроме как на сервере разработки). В реальном развертывании, /media/ как правило, будет обслуживаться статически.


Есть несколько способов добиться этого.

  • Сохраните данные в модели django:

    models.py:

     class File(models.model):
        user = models.ForeignKey(User, related_name='files', null=False)
        filename = models.CharField(max_length=255, blank=False, null=False)
        data = models.BinaryField()
      

    views.py:

     from django.shortcuts import get_object_or_404
    from django.contrib.auth.decorators import login_required
    
    @login_required
    def file(request, filename):
        file = get_object_or_404(File, user=request.user, filename=filename)
        return HttpResponse(file.data)
      
  • Сохраните данные в файловой системе, но используйте модель для их отслеживания:

    models.py:

     class File(models.model):
        user = models.ForeignKey(User, related_name='files', null=False)
        filename = models.CharField(max_length=255, blank=False, null=False)
      

    views.py:

     from django.shortcuts import get_object_or_404
    from django.contrib.auth.decorators import login_required
    
    @login_required
    def file(request, filename):
        file = get_object_or_404(File, user=request.user, filename=filename)
        return HttpResponse(open(file.filename, 'rb').read())
      

Я бы рекомендовал вариант 2, поскольку хранение большого количества двоичных данных в базе данных, подобное этому, может быть не очень хорошей идеей, но это действительно зависит от множества факторов.

В любом случае вы также должны добавлять Content-Length Last-Modified заголовки и к ответу вместе с правильным mime-типом. Я оставлю это как упражнение!

Наконец, вам нужно вызвать это представление из вашего urls.py :

 url(r'^file/(?P<filename>.*)$', 'file', name="file")
  

В вашем шаблоне предполагается my_file , что это File объект:

 <a href="{% url file my_file.filename %}">Download</a>
  

Комментарии:

1. Очень приятно, большое спасибо!!! Но как мне запустить эту функцию? Не столкнусь ли я с той же проблемой, что функция не будет запускаться, потому что URL содержит /media/ ?

2. вам нужно будет опубликовать свой urls.py

3. Тьфу, я где-то что-то делаю не так. Файлы по-прежнему можно свободно загружать, как и раньше. Я отредактирую свой OP с информацией

4. В каком модуле находится ваше представление? Вторым аргументом url(…) должен быть полный путь к вашему представлению, например, если ваше представление находится views.py в пакете app , это будет url(r'^file/(?P<filename>.*)$', 'app.views.file', name="file")

5. О, конечно. Исправлено. Но все равно ничего не происходит. Имеет ли значение, что я работаю на localhost? Возможно, этот запрос вообще не проходит через Django, и кажется, что это не так. Всплывающее окно загрузки появляется мгновенно.