New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cookiejar parses cookie value as int with empty name-value pair and Expires #67327
Comments
Something like "Set-Cookie: ; Expires=Thu, 01 Jan 1970 00:00:10 GMT" causes the resulting cookie.value to be parsed as an int. I expected either str or None as described in the documentation. Example evil server:
class MyHandler(http_server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Set-Cookie', '; Expires=Thu, 01 Jan 1970 00:00:10 GMT')
self.send_header('Set-Cookie', 'good=123.45600')
self.end_headers()
def main():
server = http_server.HTTPServer(('127.0.0.1', 8000), MyHandler)
server.serve_forever()
if __name__ == '__main__':
main()
Example innocent client:
def main():
cj = http_cookiejar.CookieJar()
opener = urllib_request.build_opener(urllib_request.HTTPCookieProcessor(cj))
r = opener.open("http://127.0.0.1:8000/")
print(cj._cookies)
if __name__ == '__main__':
main() The resulting output is: {'127.0.0.1': {'/': {'expires': Cookie(version=0, name='expires', value=10.0, port=None, port_specified=False, domain='127.0.0.1', domain_specified=False, domain_initial_dot=False, path='/', path_specified=False, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={}, rfc2109=False), 'good': Cookie(version=0, name='good', value='123.45600', port=None, port_specified=False, domain='127.0.0.1', domain_specified=False, domain_initial_dot=False, path='/', path_specified=False, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={}, rfc2109=False)}}} It gives two cookies where the first one contains name='expires', value=10.0 which is unexpected. I expected that either the bad cookie is discarded or it is accepted but the value is always a str (even if it is garbage) or None. This bug was found in my custom cookie policy where I do len(cookie.value or ''). There is also a reference on StackOverflow but I believe no Python library bug report was filed: http://stackoverflow.com/q/20325571/1524507 . This was tested on Python 2.7.8, 3.2.6, 3.3.6, and 3.4.2. |
@demian I believe this may be of interest to you. |
Attached is a fix that ignores the entire invalid cookie as defined in RFC 6265, Section 5.2. I'm also attaching patches for maintenance branches as it's a valid bug (NAME=VALUE pairs are required across all RFCs), although it would break backwards compatibility if the user was expecting invalid behaviour. |
According to RFC 6265, Section 5.2:
But "Set-Cookie: spam; Expires=Thu, 01 Jan 1970 00:00:10 GMT" is accepted. key="spam", value=None.
But "Set-Cookie: =spam; Expires=Thu, 01 Jan 1970 00:00:10 GMT" is accepted. key="", value="spam". |
I agree that the current implementation doesn't conform to standards, but do you think those cases are worth fixing as they can potentially break backwards compatibility? I think that the reported case makes sense to fix as the name/value pair are entirely unexpected. However, the current behaviour is logical for the cases that you've pointed out. |
I think that for consistency either parse empty name-value pair as key="", value=None, or ignore all non-conformed cases. For backward compatibility I prefer first way. |
There is already a test present (https://hg.python.org/cpython/file/0469af231d22/Lib/test/test_http_cookiejar.py#l1084) that ensures an unset name/value pair is ignored altogether, so I don't think that makes sense from a backwards compatibility standpoint. For consistency, I kept the functionality where nameless cookies are ignored (i.e. "=foo"). I think that while it may be breaking backwards compatibility for buggy edge cases, it's more consistent with existing functionality and actually conforms to the RFC. That said, I'm not going to argue over it heatedly, so if you'd still rather see those cases permitted, let me know and I'll change it. Valueless cookies are still permitted to keep backwards compatible as there are existing tests for that. |
This looks reasonable. |
New changeset 44c1c0cbdc06 by Serhiy Storchaka in branch '2.7': New changeset c1abcbcfefab by Serhiy Storchaka in branch '3.4': New changeset 7cc7c794d1cb by Serhiy Storchaka in branch 'default': |
As side effect the parsing is now twice faster. $ ./python -m timeit -s "from http.cookiejar import parse_ns_headers" -- "parse_ns_headers('foo=bar; Expires=Thu, 01 Jan 1970 00:00:10 GMT')"
Before: 1000 loops, best of 3: 976 usec per loop
After: 1000 loops, best of 3: 537 usec per loop |
Oh, this was incorrect example. The correct one is: $ ./python -m timeit -s "from http.cookiejar import parse_ns_headers" -- "parse_ns_headers(['foo=bar; path=/; version=1; Expires=Thu, 01 Jan 1970 00:00:10 GMT'])"
Before: 10000 loops, best of 3: 177 usec per loop
After: 10000 loops, best of 3: 104 usec per loop |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: