#15852 closed Bug (fixed)
Exception when http.parse_cookie recieves bad cookie
| Reported by: | Fredrik Stålnacke | Owned by: | nobody |
|---|---|---|---|
| Component: | HTTP handling | Version: | 1.3 |
| Severity: | Normal | Keywords: | parse_cookie |
| Cc: | joonas.kuorilehto@… | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Hi!
I'm currently having a problem where the user in some circumstances get a strange cookie (from a sub-domain that I don't control). When this cookie is parsed by django http.parse_cookie an exception occurs that causes the user to get a error message back.
I've managed to reproduce the error with the following code:
from django import cookie evil_cookie="""test=<#?xml version="1.0" encoding="utf-16"?#><#CookieData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"#><#/CookieData#>|<#?xml version="1.0" encoding="utf-16"?#><#CookieData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"#><#/CookieData#>""" http.parse_cookie(evil_cookie)
The error message that are created are:
python2.6/site-packages/django/http/__init__.pyc in parse_cookie(cookie)
461 try:
462 c = SimpleCookie()
--> 463 c.load(cookie, ignore_parse_errors=True)
464 except Cookie.CookieError:
465 # Invalid cookie
python2.6/site-packages/django/http/__init__.pyc in load(self, rawdata, ignore_parse_errors)
95 self.bad_cookies = []
96 self._BaseCookie__set = self._loose_set
---> 97 super(SimpleCookie, self).load(rawdata)
98 if ignore_parse_errors:
99 self._BaseCookie__set = self._strict_set
/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.pyc in load(self, rawdata)
623 """
624 if type(rawdata) == type(""):
--> 625 self.__ParseString(rawdata)
626 else:
627 self.update(rawdata)
/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.pyc in __ParseString(self, str, patt)
654 else:
655 rval, cval = self.value_decode(V)
--> 656 self.__set(K, rval, cval)
657 M = self[K]
658 # end __ParseString
python2.6/site-packages/django/http/__init__.pyc in _loose_set(self, key, real_value, coded_value)
105 def _loose_set(self, key, real_value, coded_value):
106 try:
--> 107 self._strict_set(key, real_value, coded_value)
108 except Cookie.CookieError:
109 self.bad_cookies.append(key)
/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.pyc in __set(self, key, real_value, coded_value)
576 """Private method for setting a cookie's value"""
577 M = self.get(key, Morsel())
--> 578 M.set(key, real_value, coded_value)
579 dict.__setitem__(self, key, M)
580 # end __set
AttributeError: 'NoneType' object has no attribute 'set'
I am using Django 1.3 on Mac OS X with Python 2.6.1
Attachments (2)
Change History (11)
by , 15 years ago
| Attachment: | patch.diff added |
|---|
comment:1 by , 15 years ago
| Easy pickings: | unset |
|---|---|
| Has patch: | set |
| Needs tests: | set |
| Patch needs improvement: | set |
| Triage Stage: | Unreviewed → Accepted |
I am not sure about what happens exactly. However, I confirmed that the bug occurs with Django's patched version of SimpleCookie and not with Python's original version.
>>> from Cookie import SimpleCookie
>>> SimpleCookie().load(evil_cookie)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.py", line 627, in load
self.__ParseString(rawdata)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.py", line 660, in __ParseString
self.__set(K, rval, cval)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.py", line 580, in __set
M.set(key, real_value, coded_value)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/Cookie.py", line 455, in set
raise CookieError("Illegal key value: %s" % key)
Cookie.CookieError: Illegal key value: xmlns:xsi
(that exception would be catched properly in Django).
The patch does not seem appropriate. We should fix the root cause and not mask it by arbitrarily catching an exception.
by , 15 years ago
| Attachment: | 15852_repeated_bad_cookie_name.diff added |
|---|
Fix failure when a bad cookie name is repeated
comment:2 by , 15 years ago
| Needs tests: | unset |
|---|
This is related to #13007.
Here is a short example:
from django import http
http.parse_cookie('a:=b; a:=c; d=e')
The problem is that when a CookieError is raised http.SimpleCookie._loose_set bypasses regular code paths to store a key whose value is None. The normal code path would ensure that the value is a Morsel object.
None works fine when the key occurs only once, so this isn't catched by the test commited in r15523.
When the same key is encountered a second time, though, this value is used in BaseCookie under the assumption that it is a Morsel instance and consequently it has a set() method. Of course, None doesn't have one, hence the bug.
The immediate fix is to use a Morsel instance. It doesn't matter if it supports httponly or not, it will be removed anyway.
Fixing this brings a second problem: bad cookies are colected in a list, to be removed when loading finishes. This will result in calling del self[key] more than once for the same key and will fail.
15852_repeated_bad_key.diff is a short patch that fixes the above.
comment:3 by , 14 years ago
| Patch needs improvement: | unset |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
comment:4 by , 14 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |
In [16452]:
(The changeset message doesn't reference this ticket)
comment:5 by , 14 years ago
| Cc: | added |
|---|---|
| Resolution: | fixed |
| Status: | closed → reopened |
| UI/UX: | unset |
I believe this bug has not been fixed in 1.3.X branch. As I have understood bug fixes like this should land into 1.3.X.
I was testing with Codenomicon HTTP Test Suite fuzzer and noticed a very similar traceback in my server logs. I can reproduce this by sending the following minimized HTTP request:
POST / HTTP/1.1
Host: 10.10.3.83
Cookie: = = = = =
{}
This causes a 'NoneType' 'AttributeError' on both Django test server and Apache mod_wsgi.
Traceback with Django 1.3.1:
Traceback (most recent call last):
File "/var/env/local/lib/python2.7/site-packages/django/core/servers/basehttp.py", line 283, in run
self.result = application(self.environ, self.start_response)
File "/var/env/local/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 68, in __call__
return self.application(environ, start_response)
File "/var/env/local/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 272, in __call__
response = self.get_response(request)
File "/var/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 169, in get_response
response = self.handle_uncaught_exception(request, resolver, sys.exc_info())
File "/var/env/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 218, in handle_uncaught_exception
return callback(request, **param_dict)
File "/var/env/local/lib/python2.7/site-packages/django/utils/decorators.py", line 89, in _wrapped_view
result = middleware.process_view(request, view_func, args, kwargs)
File "/var/env/local/lib/python2.7/site-packages/django/middleware/csrf.py", line 116, in process_view
request.META["CSRF_COOKIE"] = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME])
File "/var/env/local/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 218, in _get_cookies
self._cookies = http.parse_cookie(self.environ.get('HTTP_COOKIE', ''))
File "/var/env/local/lib/python2.7/site-packages/django/http/__init__.py", line 468, in parse_cookie
c.load(cookie, ignore_parse_errors=True)
File "/var/env/local/lib/python2.7/site-packages/django/http/__init__.py", line 97, in load
super(SimpleCookie, self).load(rawdata)
File "/usr/lib/python2.7/Cookie.py", line 632, in load
self.__ParseString(rawdata)
File "/usr/lib/python2.7/Cookie.py", line 665, in __ParseString
self.__set(K, rval, cval)
File "/var/env/local/lib/python2.7/site-packages/django/http/__init__.py", line 107, in _loose_set
self._strict_set(key, real_value, coded_value)
File "/usr/lib/python2.7/Cookie.py", line 585, in __set
M.set(key, real_value, coded_value)
AttributeError: 'NoneType' object has no attribute 'set'
I think this problem is related to this ticket. Either the fix is not included in the 1.3.X branch or the fix is incomplete.
follow-up: 7 comment:6 by , 14 years ago
| Resolution: | → fixed |
|---|---|
| Status: | reopened → closed |
Until a few months ago, all bugfixes were backported to the stable branch. This policy was recently changed in order to reduce the risk of regressions and the work load for the core team.
The new policy is documented here: https://docs.djangoproject.com/en/dev/internals/release-process/#supported-versions
comment:7 by , 14 years ago
Replying to aaugustin:
Until a few months ago, all bugfixes were backported to the stable branch. This policy was recently changed in order to reduce the risk of regressions and the work load for the core team.
The new policy is documented here: https://docs.djangoproject.com/en/dev/internals/release-process/#supported-versions
So that policy is clear. My follow-up question would be: what makes this "normal", or otherwise not "critical" in severity (which would get it into the 1.3.X branch by your policy).
My perspective on this bug, is that it would be nice if there were no reproducible ways for any kind of website user (web browser, api consumer, etc.) to cause a 500 error. OTOH, I don't have an idea of how many more tickets that would put into the same category to backport.
So, what is the criteria for various severity levels? If that's documented somewhere, I couldn't find it.
comment:8 by , 14 years ago
The criteria are given on the page already linked:
- Security issues.
- Data-loss bugs.
- Crashing bugs.
- Major functionality bugs in newly-introduced features.
The rule of thumb is that fixes will be backported to the last minor release for bugs that would have prevented a release in the first place.
Having thought about this, it seems that this would fall under 'crashing bugs' and under the last point. This causes crashes, and since this is a regression relative to Python's SimpleCookie, I think I would have marked this as a release blocker had it been found before 1.3 was released.
comment:9 by , 14 years ago
Given Luke's analysis, and since the patch applies easily to the 1.3 branch, I've backported the fix in r17168.
patched exception handling in parse_cookie method