Fixed DoS hole for fe_session_data table. See changelog for log description.
authorKasper Skårhøj <kasper@typo3.org>
Fri, 29 Apr 2005 11:33:18 +0000 (11:33 +0000)
committerKasper Skårhøj <kasper@typo3.org>
Fri, 29 Apr 2005 11:33:18 +0000 (11:33 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@696 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
TODO.txt
t3lib/config_default.php
typo3/sysext/cms/tslib/class.tslib_fe.php
typo3/sysext/cms/tslib/class.tslib_feuserauth.php

index 7df6642..4c746e6 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2005-04-29  Kasper Skårhøj,,,  <kasper@typo3.com>
 
+       * Added default limit (10kb) on frontend user session data (set by TYPO3_CONF_VARS[FE][maxSessionDataSize]) and added a check that session data is saved only if a cookie is actually set. This closes a quite obvious hole for DoS attacks where requesting a TYPO3 URL something like "...index.php?id=1&recs[foo][bar]=[up to 2000 chars]" would fill 2kb of data into fe_session_data no questions asked. It is not a security problem but thousand such request (with eg. "ab") would mean 2 megabyte of junk in the database... Spamming that table is now considerably more complicated. However this setting might break applications storing large amounts of user session data, but for the average shopping plugin it should be unaffected.
+
+2005-04-29  Kasper Skårhøj,,,  <kasper@typo3.com>
+
        * Applied fixes to t3lib_extMgm in order to prevent possible fatal errors where only local extensions would be loaded, resulting in a complete failure of the system (aka "...the cms extension is not loaded" bug). It is unknown if it fixes such problems since the bug is not repeatable.
 
 2005-04-28  Kasper Skårhøj,,,  <kasper@typo3.com>
index dc3d827..d308ff3 100755 (executable)
--- a/TODO.txt
+++ b/TODO.txt
@@ -419,7 +419,7 @@ XQCR: t3lib/class.t3lib_positionmap.php
 -QCR: t3lib/class.t3lib_readmail.php
 XQCR: t3lib/class.t3lib_recordlist.php
 -QCR: t3lib/class.t3lib_scbase.php
-               t3lib/class.t3lib_sqlengine.php                 [Karsten?]
+-QC-: t3lib/class.t3lib_sqlengine.php
 -QCR: t3lib/class.t3lib_stdgraphic.php
 XQCR: t3lib/class.t3lib_superadmin.php
 -QC-: t3lib/class.t3lib_svbase.php
index 8a307e6..5ad780d 100755 (executable)
@@ -175,12 +175,14 @@ $TYPO3_CONF_VARS = Array(
                'compressionDebugInfo' => 0,                    // Boolean. If set, then in the end of the pages, the sizes of the compressed and non-compressed document is output. This should be used ONLY as a test, because the content is compressed twice in order to output this statistics!
                'pageNotFound_handling' => '',                  // How TYPO3 should handle requests for non-existing/accessible pages. false (default): The 'nearest' page is shown. TRUE or '1': An TYPO3 error box is displayed. Strings: redirect URL, eg. 'notfound.html' or 'http://www.domain.org/errors/notfound.html'. If prefixed with "READFILE:" then it will expect the remaining string to be a HTML file which will be read and outputted directly after having the marker "###CURRENT_URL###" substituted with REQUEST_URI and ###REASON### with reason text, for example: "READFILE:fileadmin/notfound.html". Another option is the prefix "USER_FUNCTION:" which will call a user function, eg. "USER_FUNCTION:typo3conf/pageNotFoundHandling.php:user_pageNotFound->pageNotFound" where the file must contain a class "user_pageNotFound" with a method "pageNotFound" inside with two parameters, $param and $ref
                'pageNotFound_handling_statheader' => 'Status: 404 Not Found',                  // If 'pageNotFound_handling' is enabled, this string will always be sent as header before the actual handling.
+               'pageNotFoundOnCHashError' => FALSE,    // Boolean. If true, a page not found call is made when cHash evaluation errors occur. By default they will just disable caching but still display page output.
                'userFuncClassPrefix' => 'user_',               // This prefix must be the first part of any function or class name called from TypoScript, for instance in the stdWrap function.
                'addRootLineFields' => '',                              // Comma-list of fields from the 'pages'-table. These fields are added to the select query for fields in the rootline.
                'checkFeUserPid' => 1,                                  // Boolean. If set, the pid of fe_user logins must be sent in the form as the field 'pid' and then the user must be located in the pid. If you unset this, you should change the fe_users.username eval-flag 'uniqueInPid' to 'unique' in $TCA. This will do: $TCA['fe_users']['columns']['username']['config']['eval']= 'nospace,lower,required,unique';
                'lockIP' => 2,                                                  // Integer (0-4). If >0, fe_users are locked to (a part of) their REMOTE_ADDR IP for their session. Enhances security but may throw off users that may change IP during their session (in which case you can lower it to 2 or 3). The integer indicates how many parts of the IP address to include in the check. Reducing to 1-3 means that only first, second or third part of the IP address is used. 4 is the FULL IP address and recommended. 0 (zero) disables checking of course.
                'loginSecurityLevel' => '',                             // See description for TYPO3_CONF_VARS[BE][loginSecurityLevel]. Default state for frontend is "normal". Alternative authentication services can implement higher levels if preferred.
                'lifetime' => 0,                                                // Integer, positive. If >0, the cookie of FE users will NOT be a session cookie (deleted when browser is shut down) but rather a cookie with a lifetime of the number of seconds this value indicates. Setting this value to 3600*24*7 will result in automatic login of FE users during a whole week.
+               'maxSessionDataSize' => 10000,                  // Integer. Setting the maximum size (bytes) of frontend session data stored in the table fe_session_data. Set to zero (0) means no limit, but this is not recommended since it also disables a check that session data is stored only if a confirmed cookie is set.
                'lockHashKeyWords' => 'useragent',              // Keyword list (Strings commaseparated). Currently only "useragent"; If set, then the FE user session is locked to the value of HTTP_USER_AGENT. This lowers the risk of session hi-jacking. However some cases (like payment gateways) might have to use the session cookie and in this case you will have to disable that feature (eg. with a blank string).
                'defaultUserTSconfig' => '',                    // Enter lines of default frontend user/group TSconfig.
                'defaultTypoScript_constants' => '',    // Enter lines of default TypoScript, constants-field.
index 228ff50..4bfe923 100755 (executable)
                $this->fe_user->fetchSessionData();     // Gets session data
                $recs = t3lib_div::_GP('recs');
                if (is_array($recs))    {       // If any record registration is submitted, register the record.
-                       $this->fe_user->record_registration($recs);
+                       $this->fe_user->record_registration($recs, $this->TYPO3_CONF_VARS['FE']['maxSessionDataSize']);
                }
 
                        // Call hook for possible manipulation of frontend user object
                        $cHash_calc = t3lib_div::shortMD5(serialize($this->cHash_array));
 
                        if ($cHash_calc!=$this->cHash)  {
-                               $this->set_no_cache();
-                               $GLOBALS['TT']->setTSlogMessage('The incoming cHash "'.$this->cHash.'" and calculated cHash "'.$cHash_calc.'" did not match, so caching was disabled. The fieldlist used was "'.implode(',',array_keys($this->cHash_array)).'"',2);
+                               if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
+                                       $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash comparison failed)');
+                               } else {
+                                       $this->set_no_cache();
+                                       $GLOBALS['TT']->setTSlogMessage('The incoming cHash "'.$this->cHash.'" and calculated cHash "'.$cHash_calc.'" did not match, so caching was disabled. The fieldlist used was "'.implode(',',array_keys($this->cHash_array)).'"',2);
+                               }
                        }
                }
        }
         */
        function reqCHash()     {
                if (!$this->cHash)      {
-                       $this->set_no_cache();
-                       $GLOBALS['TT']->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled ',2);
+                       if ($this->TYPO3_CONF_VARS['FE']['pageNotFoundOnCHashError']) {
+                               if ($this->tempContent) { $this->clearPageCacheContent(); }
+                               $this->pageNotFoundAndExit('Request parameters could not be validated (&cHash empty)');
+                       } else {
+                               $this->set_no_cache();
+                               $GLOBALS['TT']->setTSlogMessage('TSFE->reqCHash(): No &cHash parameter was sent for GET vars though required so caching is disabled ',2);
+                       }
                }
        }
 
index c0264d2..c690abe 100644 (file)
@@ -371,28 +371,33 @@ class tslib_feUserAuth extends t3lib_userAuth {
         * If a change in the recs storage happens (which it probably does) the function setKey() is called in order to store the array again.
         *
         * @param       array           The data array to merge into/override the current recs values. The $recs array is constructed as [table]][uid] = scalar-value (eg. string/integer).
+        * @param       integer         The maximum size of stored session data. If zero, no limit is applied and even confirmation of cookie session is discarded.
         * @return      void
         */
-       function record_registration($recs)     {
-               if ($recs['clear_all']) {
-                       $this->setKey('ses','recs','');
-               }
-               $change=0;
-               $recs_array=$this->getKey('ses','recs');
-               reset($recs);
-               while(list($table,$data)=each($recs))   {
-                       if (is_array($data))    {
-                               reset($data);
-                               while(list($rec_id,$value)=each($data)) {
-                                       if ($value != $recs_array[$table][$rec_id])     {
-                                               $recs_array[$table][$rec_id] = $value;
-                                               $change=1;
+       function record_registration($recs,$maxSizeOfSessionData=0)     {
+
+                       // Storing value ONLY if there is a confirmed cookie set (->cookieID), otherwise a shellscript could easily be spamming the fe_sessions table with bogus content and thus bloat the database
+               if (!$maxSizeOfSessionData || $this->cookieId===$this->id)      {
+                       if ($recs['clear_all']) {
+                               $this->setKey('ses','recs','');
+                       }
+                       $change=0;
+                       $recs_array=$this->getKey('ses','recs');
+                       reset($recs);
+                       while(list($table,$data)=each($recs))   {
+                               if (is_array($data))    {
+                                       reset($data);
+                                       while(list($rec_id,$value)=each($data)) {
+                                               if ($value != $recs_array[$table][$rec_id])     {
+                                                       $recs_array[$table][$rec_id] = $value;
+                                                       $change=1;
+                                               }
                                        }
                                }
                        }
-               }
-               if ($change)    {
-                       $this->setKey('ses','recs',$recs_array);
+                       if ($change && (!$maxSizeOfSessionData || strlen(serialize($recs_array))<$maxSizeOfSessionData))        {
+                               $this->setKey('ses','recs',$recs_array);
+                       }
                }
        }
 }