在Django中没有调用View函数

时间:2014-06-30 09:47:16

标签: 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')

我应该注意的一个非常有趣的观察是,当我删除底部返回语句时,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 个答案:

答案 0 :(得分:2)

你的整个方法都存在缺陷。检查request.user.id匹配在网址中传递的pk只是意味着登录用户的ID必须与他们在网址中输入的ID匹配。

这不会验证该用户实际上是文件的“所有者”。

如果用户A(id = 1)拥有foo.txt且用户B(id = 2)拥有bar.txt,则没有任何内容可阻止用户A使用以下网址访问bar.txt

/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-LengthLast-Modified标头以及正确的mime类型。我会把它留作练习!

最后,您需要从urls.py

中调用此视图
url(r'^file/(?P<filename>.*)$', 'file', name="file")

在模板中,假设my_fileFile对象:

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