You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_python-commits@quetz.apache.org by jg...@apache.org on 2006/02/19 20:51:17 UTC

svn commit: r378945 - in /httpd/mod_python/branches/3.2.x: Doc/appendixc.tex NEWS lib/python/mod_python/Session.py lib/python/mod_python/__init__.py src/include/mpversion.h test/test.py

Author: jgallacher
Date: Sun Feb 19 11:51:17 2006
New Revision: 378945

URL: http://svn.apache.org/viewcvs?rev=378945&view=rev
Log:
Added check for the validity of the session id. This will fix a potential
directory traversal attack in FileSession.
Ref MODPYTHON-135

Modified:
    httpd/mod_python/branches/3.2.x/Doc/appendixc.tex
    httpd/mod_python/branches/3.2.x/NEWS
    httpd/mod_python/branches/3.2.x/lib/python/mod_python/Session.py
    httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py
    httpd/mod_python/branches/3.2.x/src/include/mpversion.h
    httpd/mod_python/branches/3.2.x/test/test.py

Modified: httpd/mod_python/branches/3.2.x/Doc/appendixc.tex
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/Doc/appendixc.tex?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/Doc/appendixc.tex (original)
+++ httpd/mod_python/branches/3.2.x/Doc/appendixc.tex Sun Feb 19 11:51:17 2006
@@ -1,3 +1,17 @@
+\chapter{Changes from Version (3.2.7)\label{app-changes}}
+
+\indexii{Changes from}{version 3.2.7}
+
+  Security Fix 
+
+  \begin{itemize}
+    \item
+    Fixed possible directory traversal attack in FileSession. The session
+    id is now checked to ensure it only contains valid characters. This
+    check is performed for all sessions derived from the BaseSession 
+    class.
+  \end{itemize}
+ 
 \chapter{Changes from Version (3.1.4)\label{app-changes}}
 
 \indexii{Changes from}{version 3.1.4}

Modified: httpd/mod_python/branches/3.2.x/NEWS
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/NEWS?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/NEWS (original)
+++ httpd/mod_python/branches/3.2.x/NEWS Sun Feb 19 11:51:17 2006
@@ -1,3 +1,7 @@
+Feb 19 2006 -	3.2.8 is being tagged
+                3.2.8 is a security release to fix the possible
+		directory traversal attack in FileSession.
+
 Feb  3 2006 -	3.2.7 is being tagged
 
 Jan 16 2006 -	3.2.6 final is being tagged (no changes from 3.2.6b)

Modified: httpd/mod_python/branches/3.2.x/lib/python/mod_python/Session.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/lib/python/mod_python/Session.py?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/lib/python/mod_python/Session.py (original)
+++ httpd/mod_python/branches/3.2.x/lib/python/mod_python/Session.py Sun Feb 19 11:51:17 2006
@@ -29,6 +29,7 @@
 import cPickle, cStringIO
 import tempfile
 import traceback
+import re
 
 COOKIE_NAME="pysid"
 DFT_TIMEOUT=30*60 # 30 min
@@ -82,6 +83,19 @@
     # Make a number based on current time, pid, remote ip
     # and two random ints, then hash with md5. This should
     # be fairly unique and very difficult to guess.
+    #
+    # WARNING
+    # The current implementation of _new_sid returns an
+    # md5 hexdigest string. To avoid a possible directory traversal
+    # attack in FileSession the sid is validated using
+    # the _check_sid() method and the compiled regex
+    # validate_sid_re. The sid will be accepted only if len(sid) == 32
+    # and it only contains the characters 0-9 and a-f. 
+    #
+    # If you change this implementation of _new_sid, make sure to also
+    # change the validation scheme, as well as the test_Session_illegal_sid()
+    # unit test in test/test.py.
+    # /WARNING
 
     t = long(time.time()*10000)
     pid = os.getpid()
@@ -92,6 +106,21 @@
 
     return md5.new("%d%d%d%d%s" % (t, pid, rnd1, rnd2, ip)).hexdigest()
     
+validate_sid_re = re.compile('[0-9a-f]{32}$')
+
+def _check_sid(sid):
+    ## Check the validity of the session id
+    # # The sid must be 32 characters long, and consisting of the characters
+    # 0-9 and a-f.
+    # 
+    # The sid may be passed in a cookie from the client and as such
+    # should not be trusted. This is particularly important in
+    # FileSession, where the session filename is derived from the sid.
+    # A sid containing '/' or '.' characters could result in a directory
+    # traversal attack
+    
+    return not not validate_sid_re.match(sid)
+
 class BaseSession(dict):
 
     def __init__(self, req, sid=None, secret=None, lock=1,
@@ -120,6 +149,14 @@
 
             if cookies.has_key(session_cookie_name):
                 self._sid = cookies[session_cookie_name].value
+        
+        if self._sid:
+            # Validate the sid *before* locking the session
+            # _check_sid will raise an apache.SERVER_RETURN exception 
+            if not _check_sid(self._sid):
+                self._req.log_error("mod_python.Session warning: The session id contains invalid characters, valid characters are only 0-9 and a-f",
+                        apache.APLOG_WARNING)
+                raise apache.SERVER_RETURN, apache.HTTP_INTERNAL_SERVER_ERROR
 
         self.init_lock()
 

Modified: httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py (original)
+++ httpd/mod_python/branches/3.2.x/lib/python/mod_python/__init__.py Sun Feb 19 11:51:17 2006
@@ -20,5 +20,5 @@
 __all__ = ["apache", "cgihandler", "psp",
            "publisher", "util", "python22"]
 
-version = "3.2.7"
+version = "3.2.8"
 

Modified: httpd/mod_python/branches/3.2.x/src/include/mpversion.h
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/src/include/mpversion.h?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/src/include/mpversion.h (original)
+++ httpd/mod_python/branches/3.2.x/src/include/mpversion.h Sun Feb 19 11:51:17 2006
@@ -1,5 +1,5 @@
 #define MPV_MAJOR 3
 #define MPV_MINOR 2
-#define MPV_PATCH 7
-#define MPV_BUILD 20060202
-#define MPV_STRING "3.2.7"
+#define MPV_PATCH 8
+#define MPV_BUILD 20060219
+#define MPV_STRING "3.2.8"

Modified: httpd/mod_python/branches/3.2.x/test/test.py
URL: http://svn.apache.org/viewcvs/httpd/mod_python/branches/3.2.x/test/test.py?rev=378945&r1=378944&r2=378945&view=diff
==============================================================================
--- httpd/mod_python/branches/3.2.x/test/test.py (original)
+++ httpd/mod_python/branches/3.2.x/test/test.py Sun Feb 19 11:51:17 2006
@@ -1595,10 +1595,47 @@
         response = conn.getresponse()
         rsp = response.read()
         conn.close()
-
         if rsp != "test ok":
             self.fail("session did not accept our cookie")
 
+    def test_Session_illegal_sid_conf(self):
+
+        c = VirtualHost("*",
+                        ServerName("test_Session_Session"),
+                        DocumentRoot(DOCUMENT_ROOT),
+                        Directory(DOCUMENT_ROOT,
+                                  SetHandler("mod_python"),
+                                  PythonHandler("tests::Session_Session"),
+                                  PythonDebug("On")))
+        return str(c)
+
+    def test_Session_illegal_sid(self):
+
+        print "\n  * Testing Session with illegal session id value"
+        bad_cookie = 'pysid=/path/traversal/attack/bad; path=/'
+        conn = httplib.HTTPConnection("127.0.0.1:%s" % PORT)
+        conn.putrequest("GET", "/tests.py", skip_host=1)
+        conn.putheader("Host", "test_Session_Session:%s" % PORT)
+        conn.putheader("Cookie", bad_cookie)
+        conn.endheaders()
+        response = conn.getresponse()
+        status = response.status
+        conn.close()
+        if status != 500:
+            self.fail("session accepted a sid with illegal characters")
+
+        bad_cookie = 'pysid=%s; path=/' % ('abcdef'*64)
+        conn = httplib.HTTPConnection("127.0.0.1:%s" % PORT)
+        conn.putrequest("GET", "/tests.py", skip_host=1)
+        conn.putheader("Host", "test_Session_Session:%s" % PORT)
+        conn.putheader("Cookie", bad_cookie)
+        conn.endheaders()
+        response = conn.getresponse()
+        status = response.status
+        conn.close()
+        if status != 500:
+            self.fail("session accepted a sid which is too long")
+
     def test_publisher_conf(self):
         c = VirtualHost("*",
                         ServerName("test_publisher"),
@@ -1970,6 +2007,7 @@
         perRequestSuite.addTest(PerRequestTestCase("test_Cookie_Cookie"))
         perRequestSuite.addTest(PerRequestTestCase("test_Cookie_MarshalCookie"))
         perRequestSuite.addTest(PerRequestTestCase("test_Session_Session"))
+        perRequestSuite.addTest(PerRequestTestCase("test_Session_illegal_sid"))
         perRequestSuite.addTest(PerRequestTestCase("test_interpreter_per_directive"))
         perRequestSuite.addTest(PerRequestTestCase("test_interpreter_per_directory"))
         perRequestSuite.addTest(PerRequestTestCase("test_publisher"))