【问题标题】:django modifying the request objectdjango 修改请求对象
【发布时间】:2013-09-21 07:48:57
【问题描述】:

我已经有一个 django 项目,它的逻辑如下:

url: URL?username=name&pwd=passwd

查看:

def func(request):
   dic = request.GET

   username = dic.get("username")
   pwd = dic.get("pwd")

但现在我们需要加密数据。然后,请求变成这样:

url: URL?crypt=XXXXXXXXXX (XXXXXXXXX 是加密的 str 为 "username=name&pwd=passwd")

所以我需要修改每个视图函数。但现在我想在 django 中间件中解密,以防止修改每个视图函数。

但是当我修改 request.GET 时,我收到错误消息“此 QueryDict 实例是不可变的”。如何修改?

【问题讨论】:

标签: python django


【解决方案1】:

分配给request.GETrequest.POSTdjango.http.QueryDict 对象是不可变的。

您可以通过复制将其转换为可变的QueryDict 实例:

request.GET = request.GET.copy()

之后你就可以修改QueryDict:

>>> from django.test.client import RequestFactory
>>> request = RequestFactory().get('/')
>>> request.GET
<QueryDict: {}>
>>> request.GET['foo'] = 'bar'
AttributeError: This QueryDict instance is immutable
>>> request.GET = request.GET.copy()
<QueryDict: {}>
>>> request.GET['foo'] = 'bar'
>>> request.GET
<QueryDict: {'foo': 'bar'}>

这是有意设计的,因此不允许任何应用程序组件编辑源请求数据,因此即使再次创建不可变的QueryDict 也会破坏这种设计。我仍然建议您遵循指南并直接在中间件中的 request 对象上分配额外的请求数据,尽管这可能会导致您编辑源代码。

【讨论】:

  • 在 Django 2.0 中,当我尝试这样做时,我得到一个 AttributeError "can't set attribute"
【解决方案2】:

移除不变性:

if not request.GET._mutable:
   request.GET._mutable = True

# now you can spoil it
request.GET['pwd'] = 'iloveyou'

更新

Django 认可的方式是:request.GET.copy().

根据docs

在正常的请求/响应周期中访问时,request.POST 和 request.GET 中的 QueryDicts 将是不可变的。要获得可变版本,您需要使用 QueryDict.copy()。

没有什么可以保证未来的 Django 版本会使用 _mutable。这比 copy() 方法有更多改变的机会。

【讨论】:

  • 这样可以更好地避免与 POST 请求中的 copy() 相关的成本
  • ...但更糟糕的是,它修改了内部属性。
  • 设置值后,您可以执行以下操作:request.GET._mutable = False 使其再次变为不可变。
  • 请不要推荐这样做。
  • 谁能告诉我“这种方法安全吗?”
【解决方案3】:

您不应该使用 GET 来发送用户名和密码,这是不好的做法(因为它会在 URL 栏上显示信息,并且可能会构成 security risk)。相反,使用 POST。另外,我猜您正在尝试对您的用户进行身份验证,并且您似乎在做太多工作(创建一个新的中间件)来处理完全内置的东西,以from the docs 为例:

from django.contrib.auth import authenticate, login

def my_view(request):
    username = request.POST['username']
    password = request.POST['password']
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            login(request, user)
            # Redirect to a success page.
        else:
            # Return a 'disabled account' error message
    else:
        # Return an 'invalid login' error message.

我自己非常喜欢使用login_required decorator,非常简单易用。希望有帮助

【讨论】:

  • 感谢您的回答。这只是一个例子。我们不仅使用 GET,还使用 ​​POST。即使使用post,我们也必须对数据进行加密。因为我害怕像'tcpdump'这样的工具抓取数据包。所以,加密/解密是必要的。让我困惑的问题是如何尽可能少地修改视图功能。所以我想使用中间件
  • 查看有关创建自己的中间件的文档。您想使用 process_request 或 process_view,但请记住,在其中访问 request.POST 会导致视图无法运行(csrf 是该规则的一个例外,因此请研究它如何工作以弄清楚如何编写自己的) docs.djangoproject.com/en/dev/topics/http/middleware
  • GET 请求本身不存在安全问题。这是一种不好的做法,因为它们位于地址栏中……但我只想确保每个人都清楚通过 HTTPS,GET 和 POST 有效负载在传输过程中受到同等保护。通过 HTTP,它们同样不受保护。
  • @AdamNelson 如果您将任何内容传递给 GET 请求,那么它将在日志文件、互联网历史记录和重定向 HTTP 标头中可用。它根本不安全,即使是通过 HTTPS。它们在运输过程中没有受到同等保护。
  • @Jordan 日志文件和 Internet 历史记录部分是合法的侧通道攻击,您是对的,它们使 GET 比 POST 更差安全性,但是 HTTPS 信封内的保护是完全一样的发布或获取
【解决方案4】:
request.GET._mutable = True

你需要这个。

def func(request):
   dic = request.GET
   request.GET._mutable = True #to make it editable 
   username = dic.get("username")
   request.GET.pop("pwd")
   request.GET._mutable = False #make it False once edit done

【讨论】:

    【解决方案5】:

    您只需要更改 request.data:

    def func(request):
       request.data._mutable = True
       dic = request.data
       username = dic['username']
       pwd = dic['pwd']
    

    【讨论】:

      【解决方案6】:

      目前,我有 Django 3.2.2。我需要修改self.request.query_params。我在 cmets 中读到 here self.request.query_params 只是 self.request.GET 的包装。

      首先,我尝试了以下方法:

      self.request.GET = self.request.GET.copy()
      ... modify self.request.GET
      

      但是self.request.query_params 没有改变。

      当我明确尝试self.request.query_params = self.request.GETself.request.query_params = self.request.query_params.copy() 时,我得到了AttributeError: can't set attribute

      唯一有效的方法:

      self.request.query_params._mutable = True
      self.request.query_params['attribute'] = 'needed_value'
      self.request.query_params._mutable = False
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多