82

I already have a django project and it logical like those:

url: URL?username=name&pwd=passwd

view:

def func(request):
   dic = request.GET

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

but now we need encrypt the data. Then, the request become this:

url: URL?crypt=XXXXXXXXXX (XXXXXXXX is encrypted str for "username=name&pwd=passwd")

so I need modify every view function. But now I want decrypt in django middleware to prevent from modifying every view function.

but when I modify request.GET, I recive error msg "This QueryDict instance is immutable". How can I modify it?

dan-klasson
  • 13,734
  • 14
  • 63
  • 101
user2801567
  • 821
  • 1
  • 6
  • 5

5 Answers5

130

django.http.QueryDict objects that are assigned to request.GET and request.POST are immutable.

You can convert it to a mutable QueryDict instance by copying it:

request.GET = request.GET.copy()

Afterwards you'll be able to modify the 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'}>

This has been purposefully designed so that none of the application components are allowed to edit the source request data, so even creating a immutable QueryDict again would break this design. I would still suggest that you follow the guidelines and assign additional request data directly on the request object in your middleware, despite the fact that it might cause you to edit your sources.

LostMyGlasses
  • 3,074
  • 20
  • 28
Filip Dupanović
  • 32,650
  • 13
  • 84
  • 114
68

Remove immutability:

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

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

Update

The Django sanctioned way is: request.GET.copy().

According to the docs:

The QueryDicts at request.POST and request.GET will be immutable when accessed in a normal request/response cycle. To get a mutable version you need to use QueryDict.copy().

Nothing guarantees future Django versions will use _mutable. This has more chances to change than the copy() method.

djvg
  • 11,722
  • 5
  • 72
  • 103
laffuste
  • 16,287
  • 8
  • 84
  • 91
9

You shouldn't use GET to send the username and password, it's bad practice (since it shows the information on the URL bar, and might pose a security risk). Instead, use POST. Also, I'm guessing you're trying to authenticate your users, and it seems like you're doing too much work (creating a new middleware) to deal with something that is completely built in, to take the example 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.

I myself really like using the login_required decorator, very simple to use. Hope that helps

Community
  • 1
  • 1
yuvi
  • 18,155
  • 8
  • 56
  • 93
  • thanks for your answer. that's just a example. We not only use GET but also use POST. Even though use post, we must encrypt the data.Because I'm afraid some tools like 'tcpdump' crawl packet.So, encrypt/decrypt is necessary.The question Confusing me is how can I modify view function as less as possible. So I want use middleware – user2801567 Sep 21 '13 at 08:25
  • Look into the documentation about creating your own middleware. You want to use process_request or process_view, but keep in mind that accessing the request.POST inside those would cause the view not to run (csrf is an exception to that rule, so look into how it works to figure how to write your own) https://docs.djangoproject.com/en/dev/topics/http/middleware/ – yuvi Sep 21 '13 at 08:37
  • Also, not sure how much that is relevant but take a look at this: https://pypi.python.org/pypi/django-secure – yuvi Sep 21 '13 at 08:40
  • 1
    There is no security problem with GET request per se. It's kind of bad practice because they're in the address bar ... but I just want to make sure that everybody is clear that over HTTPS, GET and POST payloads are equally protected in transit. Via HTTP, they are equally unprotected. – Adam Nelson Sep 27 '16 at 19:08
  • @AdamNelson You're not entirely correct, but I revised my answer to be more accurate nonetheless – yuvi Sep 27 '16 at 19:44
  • 1
    @AdamNelson if you pass anything into a GET request then it becomes available in log files, internet history, and the redirect HTTP header. It's not secure at all, even over HTTPS. They are not equally protected in transit. – Jordan Oct 26 '16 at 18:05
  • 1
    @Jordan the log file and Internet history parts are legitimate side channel attacks and you're right that they make GET worse than POST for security, but the protection inside the HTTPS envelope is exactly the same whether it's POST or GET – Adam Nelson Oct 28 '16 at 14:48
4
request.GET._mutable = True

you need this.

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
Mohideen bin Mohammed
  • 18,813
  • 10
  • 112
  • 118
0

You just have to change the request.data:

def func(request):
   request.data._mutable = True
   dic = request.data
   username = dic['username']
   pwd = dic['pwd']
betelgeuse
  • 1,136
  • 3
  • 13
  • 25