classification
Title: wsgiref.handlers.CGIHandler caches os.environ, leaking info between requests
Type: security Stage:
Components: Library (Lib) Versions: Python 3.2, Python 3.1, Python 2.7, Python 2.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: pje Nosy List: pje, snprbob86 (2)
Priority: high Keywords 26backport, easy, patch

Created on 2009-11-02 07:34 by snprbob86, last changed 2009-11-05 15:39 by pje.

Messages (8)
msg94819 - (view) Author: Brandon Bloom (snprbob86) Date: 2009-11-02 07:34
This issue came up while doing Google App Engine development. Apparently 
the default wsgi handler logic is to cache os.environ into os_environ at 
import time. This is reasonable behavior for wsgi, but when using cgi, 
this is a serious security hole which leaks information between requests.

See this related bug at GAE:
http://code.google.com/p/googleappengine/issues/detail?
id=2040&q=cookies%20dev_appserver.py&colspec=ID%20Type%20Status%20Priority
%20Stars%20Owner%20Summary%20Log%20Component
msg94867 - (view) Author: Phillip J. Eby (pje) Date: 2009-11-03 19:05
This is not an issue with CGI, but an issue with using an inappropriate
WSGI handler for a long-running process environment that only emulates CGI.

That is, in a true CGI environment, there can't be *multiple* requests
made to CGIHandler, and so it can't leak.  In "normal" (i.e. pre-GAE)
long-running web environments, os.environ would not contain any request
information, only the process startup environment.

This limitation of CGIHandler should be better documented, but it's a
natural consequence of its design.  The os_environ and base_environ
variables are provided so that subclasses with specialized needs can
handle them differently, and GAE is definitely a specialized need.

If someone wants to provide a GAEHandler class, great; otherwise, the
documented way to run a WSGI app under GAE is the
google.appengine.ext.webapp.util.run_wsgi_app function.
msg94870 - (view) Author: Brandon Bloom (snprbob86) Date: 2009-11-03 20:58
> That is, in a true CGI environment, there can't be *multiple* requests
> made to CGIHandler, and so it can't leak.  In "normal" (i.e. pre-GAE)
> long-running web environments, os.environ would not contain any request
> information, only the process startup environment.

That's fair. In this case the CGIHandler should raise an exception on
subsequent requests to prevent this programming error.

> If someone wants to provide a GAEHandler class, great; otherwise, the
> documented way to run a WSGI app under GAE is the
> google.appengine.ext.webapp.util.run_wsgi_app function.

I'm not sure if run_wsgi_app was available right from the start, as
some early tutorials and samples show using CGIHandler. That's how we
ran into this issue.
msg94871 - (view) Author: Phillip J. Eby (pje) Date: 2009-11-03 21:13
Hm.  In retrospect, CGIHandler should probably just set os_environ to an
empty dictionary in its class body (thereby not using the cached
environ), and this would then work correctly for repeated uses.

This would be a clean bugfix and wouldn't affect behavior for any
existing uses of CGIHandler that weren't already worse broken than the
GAE case.  ;-)
msg94880 - (view) Author: Brandon Bloom (snprbob86) Date: 2009-11-04 07:14
> Hm.  In retrospect, CGIHandler should probably just set os_environ to an
> empty dictionary in its class body (thereby not using the cached
> environ), and this would then work correctly for repeated uses.
>
> This would be a clean bugfix and wouldn't affect behavior for any
> existing uses of CGIHandler that weren't already worse broken than the
> GAE case.  ;-)

Yup, rock on with that :-)
msg94893 - (view) Author: Phillip J. Eby (pje) Date: 2009-11-04 16:43
I've forwarded the suggested fix to the GAE team; it is to add this line:

    os_environ = {}     # Handle GAE and other multi-run CGI use cases

to the class body of CGIHandler.  This should also be done in the Python
stdlib.

Btw, this fix can also be applied as a monkeypatch, by simply doing
'CGIHandler.os_environ = {}' before using CGIHandler.
msg94924 - (view) Author: Brandon Bloom (snprbob86) Date: 2009-11-05 10:40
> I've forwarded the suggested fix to the GAE team; it is to add this line:
>
>    os_environ = {}     # Handle GAE and other multi-run CGI use cases
>
> to the class body of CGIHandler.  This should also be done in the Python
> stdlib.

I don't think that works. This still shares a common os_environ across
requests. You need to clear the dictionary after every request.
msg94934 - (view) Author: Phillip J. Eby (pje) Date: 2009-11-05 15:39
The os_environ attribute is only used in one place; this line:

   env = self.environ = self.os_environ.copy()

So, nothing is shared.
History
Date User Action Args
2009-11-05 15:39:49pjesetmessages: + msg94934
2009-11-05 10:40:30snprbob86setmessages: + msg94924
2009-11-04 16:43:32pjesetkeywords: + patch, easy, 26backport

messages: + msg94893
2009-11-04 07:14:29snprbob86setmessages: + msg94880
2009-11-03 21:13:55pjesetmessages: + msg94871
2009-11-03 20:58:14snprbob86setmessages: + msg94870
2009-11-03 19:05:17pjesetmessages: + msg94867
2009-11-02 23:20:11pitrousetpriority: high
assignee: pje

nosy: + pje
versions: + Python 2.6, Python 3.1, Python 2.7, Python 3.2, - Python 2.5
2009-11-02 07:34:03snprbob86create