简体   繁体   English

如何防止用户更改 URL<pk> 查看其他提交数据 Django</pk>

[英]How to prevent user changing URL <pk> to see other submission data Django

I'm new to the web development world, to Django, and to applications that require securing the URL from users that change the foo/bar/ pk to access other user data.我是 web 开发世界、Django 以及需要保护 URL 的应用程序的新手,这些用户更改 foo/bar/ pk以访问其他用户数据。

Is there a way to prevent this?有没有办法防止这种情况? Or is there a built-in way to prevent this from happening in Django?或者是否有一种内置方法可以防止这种情况在 Django 中发生?

Eg: foo/bar/22 can be changed to foo/bar/14 and exposes past users data.例如: foo/bar/22可以更改为foo/bar/14并公开过去的用户数据。

I have read the answers to several questions about this topic and I have had little luck in an answer that can clearly and coherently explain this and the approach to prevent this.我已经阅读了有关该主题的几个问题的答案,但我运气不佳,无法清楚,连贯地解释这一点以及防止这种情况的方法。 I don't know a ton about this so I don't know how to word this question to investigate it properly.我对此不太了解,所以我不知道如何表达这个问题以正确调查它。 Please explain this to me like I'm 5.请像我 5 岁一样向我解释。

There are a few ways you can achieve this: 有几种方法可以实现这一目标:

If you have the concept of login, just restrict the URL to: 如果您有登录的概念,只需将URL限制为:

/foo/bar/

and in the code, user=request.user and display data only for the logged in user. 并且在代码中, user=request.user并仅显示登录用户的数据。

Another way would be: 另一种方式是:

/foo/bar/{{request.user.id}}/

and in the view: 并在视图中:

def myview(request, id):
    if id != request.user.id:
        HttpResponseForbidden('You cannot view what is not yours') #Or however you want to handle this

You could even write a middleware that would redirect the user to their page /foo/bar/userid - or to the login page if not logged in. 您甚至可以编写一个中间件 ,将用户重定向到他们的页面/foo/bar/userid - 如果没有登录,则写入登录页面。

I'd recommend using django-guardian if you'd like to control per-object access. 如果你想控制每个对象的访问,我建议使用django-guardian Here's how it would look after configuring the settings and installing it (this is from django-guardian's docs ): 以下是配置设置和安装后的样子(这是来自django-guardian的文档 ):

>>> from django.contrib.auth.models import User
>>> boss = User.objects.create(username='Big Boss')
>>> joe = User.objects.create(username='joe')
>>> task = Task.objects.create(summary='Some job', content='', reported_by=boss)
>>> joe.has_perm('view_task', task)
False

If you'd prefer not to use an external library, there's also ways to do it in Django's views . 如果您不想使用外部库,也可以在Django的视图中进行操作

Here's how that might look: 这可能是这样的:

from django.http import HttpResponseForbidden
from .models import Bar

def view_bar(request, pk):
    bar = Bar.objects.get(pk=pk)
    if not bar.user == request.user:
        return HttpResponseForbidden("You can't view this Bar.")
    # The rest of the view goes here...

Just check that the object retrieved by the primary key belongs to the requesting user. 只需检查主键检索的对象是否属于请求用户。 In the view this would be 在视图中这将是

if some_object.user == request.user: ...

This requires that the model representing the object has a reference to the User model. 这要求表示对象的模型具有对User模型的引用。

You're going to want to look into user authentication and authorization, which are both supplied by Django's Auth package . 您将要查看用户身份验证和授权,这两者都是由Django的Auth软件包提供的 There's a big difference between the two things, as well. 这两件事之间也存在很大差异。

Authentication is making sure someone is who they say they are. 身份验证确保某人是他们所说的人。 Think, logging in. You get someone to entire their user name and password to prove they are the owner of the account. 想一想,登录。你会得到一个人的全部用户名和密码来证明他们是该帐户的所有者。

Authorization is making sure that someone is able to access what they are trying to access. 授权确保某人能够访问他们尝试访问的内容。 So, a normal user for instance, won't be able to just switch PK's. 因此,例如普通用户将无法仅切换PK。

Authorization is well documented in the link I provided above. 我在上面提供的链接中详细记录了授权。 I'd start there and run through some of the sample code. 我将从那里开始并运行一些示例代码。 Hopefully that answers your question. 希望能回答你的问题。 If not, hopefully it provides you with enough information to come back and ask a more specific question. 如果没有,希望它能为您提供足够的信息,让您回来询问更具体的问题。

In django, the currently logged in user is available in your views as the property user of the request object. 在django中,当前登录的用户在您的视图中可用作请求对象的属性user

The idea is to filter your models by the logged in user first, and then if there are any results only show those results. 我们的想法是首先按登录用户过滤模型,然后如果有任何结果只显示这些结果。

If the user is trying to access an object that doesn't belong to them, don't show the object. 如果用户尝试访问不属于它们的对象,请不要显示该对象。

One way to take care of all of that is to use the get_object_or_404 shortcut function, which will raise a 404 error if an object that matches the given parameters is not found. 处理所有这些的一种方法是使用get_object_or_404快捷方式函数,如果找不到与给定参数匹配的对象,则会引发404错误。

Using this, we can just pass the primary key and the current logged in user to this method, if it returns an object, that means the primary key belongs to this user, otherwise it will return a 404 as if the page doesn't exist. 使用这个,我们可以将主键和当前登录用户传递给此方法,如果它返回一个对象,这意味着主键属于该用户,否则它将返回404,就像页面不存在一样。

Its quite simple to plug it into your view: 将它插入您的视图非常简单:

from django.shortcuts import get_object_or_404, render

from .models import YourModel

def some_view(request, pk=None):
    obj = get_object_or_404(YourModel, pk=pk, user=request.user)
    return render(request, 'details.html', {'object': obj})

Now, if the user tries to access a link with a pk that doesn't belong to them, a 404 is raised. 现在,如果用户尝试访问具有不属于它们的pk的链接,则会引发404。

In my project, for several models/tables, a user should only be able to see data that he/she entered, and not data that other users entered. 在我的项目中,对于多个模型/表,用户应该只能看到他/她输入的数据,而不能看到其他用户输入的数据。 For these models/tables, there is a user column. 对于这些模型/表,有一个用户列。

In the list view, that is easy enough to implement, just filter the query set passed to the list view for model.user = loggged_id.user. 在列表视图中,这很容易实现,只需过滤传递给model.user = loggged_id.user的列表视图的查询集。

But for the detail/update/delete views, seeing the PK up there in the URL, it is conceivable that user could edit the PK in the URL and access another user's row/data. 但是对于详细/更新/删除视图,在URL中看到PK,可以想象用户可以编辑URL中的PK并访问另一个用户的行/数据。

I'm using Django's built in class based views. 我正在使用Django基于类的内置视图。

The views with PK in the URL already have the LoginRequiredMixin, but that does not stop a user from changing the PK in the URL. URL中的PK视图已经具有LoginRequiredMixin,但这并不能阻止用户更改URL中的PK。

My solution: "Does Logged In User Own This Row Mixin" (DoesLoggedInUserOwnThisRowMixin) -- override the get_object method and test there. 我的解决方案:“登录用户拥有此行Mixin”(DoesLoggedInUserOwnThisRowMixin) - 覆盖get_object方法并在那里进行测试。

from django.core.exceptions import PermissionDenied

class DoesLoggedInUserOwnThisRowMixin(object):

    def get_object(self):
        '''only allow owner (or superuser) to access the table row'''
        obj = super(DoesLoggedInUserOwnThisRowMixin, self).get_object()
        if self.request.user.is_superuser:
            pass
        elif obj.iUser != self.request.user:
            raise PermissionDenied(
                "Permission Denied -- that's not your record!")
        return obj

Voila! 瞧!

Just put the mixin on the view class definition line after LoginRequiredMixin, and with a 403.html template that outputs the message, you are good to go. 只需将mixin放在LoginRequiredMixin之后的视图类定义行上,并使用输出消息的403.html模板,就可以了。

This is a recurring question and also implies a serious security flaw.这是一个反复出现的问题,也意味着一个严重的安全漏洞。 My contribution is this:我的贡献是这样的:

There are 2 basic aspects to take care of.有2个基本方面需要注意。

The first is the view:首先是观点:

a) Take care to add a decorator to the function-based view (such as @login_required) or a mixin to the class-based function (such as LoginRequiredMixin). a) 注意向基于函数的视图添加装饰器(例如@login_required)或向基于类的 function(例如 LoginRequiredMixin)添加混合。 I find the official Django documentation quite helpful on this ( https://docs.djangoproject.com/en/4.0/topics/auth/default/ ).我发现官方 Django 文档对此非常有帮助( https://docs.djangoproject.com/en/4.0/topics/auth/default/ )。

b) When, in your view, you define the data to be retrieved or inserted (GET or POST methods), the data of the user must be filtered by the ID of that user. b) 在您看来,当您定义要检索或插入的数据(GET 或 POST 方法)时,必须按该用户的 ID 过滤用户的数据。 Something like this:像这样的东西:

 def get(self, request, *args, **kwargs):
     self.object = self.get_object(queryset=User.objects.filter(pk=self.request.user.id))
     return super().get(request, *args, **kwargs)

The second aspect is the URL:第二个方面是URL:

In the URL you should also limit the URL to the pk that was defined in the view.在 URL 中,您还应该将 URL 限制为视图中定义的 pk。 Something like this:像这样的东西:

path('int:pk/blog-add/', AddBlogView.as_view(), name='blog-add'), path('int:pk/blog-add/', AddBlogView.as_view(), name='blog-add'),

In my experience, this prevents that an user sees the data of another user, simply by changing a number in the URL.根据我的经验,这可以防止用户看到另一个用户的数据,只需更改 URL 中的数字即可。

Hope it helps.希望能帮助到你。

In django CBV (class based views) you can prevent this by comparing the user entered pk and the current logged in user:在 django CBV(基于类的视图)中,您可以通过比较用户输入的 pk 和当前登录的用户来防止这种情况:

Note: I tested it in django 4 and python 3.9.注:我在django 4和python 3.9测试过。

from django.http import HttpResponseForbidden

class UserDetailView(LoginRequiredMixin, DetailView):
    model = your_model

    def dispatch(self, request, *args, **kwargs):
    
        if kwargs.get('pk') != self.request.user.pk:
            return HttpResponseForbidden(_('You do not have permission to     view this page'))
    
        return super().dispatch(request, *args, **kwargs)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM