* Added full safety for the internal TYPO3 formmail; email addresses in the fields...
authorKasper Skårhøj <kasper@typo3.org>
Thu, 1 Apr 2004 14:38:03 +0000 (14:38 +0000)
committerKasper Skårhøj <kasper@typo3.org>
Thu, 1 Apr 2004 14:38:03 +0000 (14:38 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@183 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/config_default.php
typo3/sysext/cms/tslib/class.tslib_content.php
typo3/sysext/cms/tslib/class.tslib_fe.php
typo3/wizard_forms.php

index 2f63d76..cd2fd44 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2004-04-01  Kasper Skårhøj,,,  <kasper@typo3.com>
 
+       * Added full safety for the internal TYPO3 formmail; email addresses in the fields "recipient" and "recipient_copy" are encrypted in the transfer and the "formmail" interface is safe for spam-misuse (since a proper recipient address cannot be forged automatically). 
+!!!If someone are using the "formmail" API from other renderers than the FORM cObject in TypoScript you will have to set the recipient address to the encrypted value by default OR alternatively disabled the check by TYPO3_CONF_VARS[FE][strictFormmail] = FALSE
+
+2004-04-01  Kasper Skårhøj,,,  <kasper@typo3.com>
+
        * Added Jens Ellerbrocks suggestion for "postUserFuncInt" in stdWrap.
 
 2004-04-01  Kasper Skårhøj,,,  <kasper@typo3.com>
index dc4920b..dbd4225 100755 (executable)
@@ -49,7 +49,7 @@ $TYPO3_CONF_VARS = Array(
        ),
        'SYS' => Array(                 // System related concerning both frontend and backend.
                'sitename' => 'TYPO3',                                  // Name of the base-site. This title shows up in the root of the tree structure if you're an 'admin' backend user.
-               'encryptionKey' => '',                                  // Insert some unique string here! Used in eg. direct mail module to generate a md5 hash in combination with uid. This string should be kept secret although it's not as critical as a password.
+               'encryptionKey' => '',                                  // This is a "salt" used for various kinds of encryption, CRC checksums and validations. You can enter any rubbish string here but try to keep it secret. You should notice that a change to this value might invalidate temporary information, URLs etc. At least, clear all cache if you change this so any such information can be rebuild with the new key.
                'doNotCheckReferer' => 0,                               // Boolean. If set, it's NOT checked numerous places that the refering host is the same as the current. This is an option you should set if you have problems with proxies not passing the HTTP_REFERER variable.
                'recursiveDomainSearch' => 0,                   // Boolean. If set, the search for domain records will be done recursively by stripping parts of the host name off until a matching domain record is found.
                'devIPmask' => '192.168.*,127.0.0.1',   // Defines a list of IP addresses which will allow development-output to display. The debug() function will use this as a filter. See the function t3lib_div::cmpIP() for details on syntax. Setting this to blank value will deny all. Setting to '*' will allow all.
@@ -154,6 +154,7 @@ $TYPO3_CONF_VARS = Array(
                'debug' => 0,                                                   // Boolean. If set, some debug HTML-comments may be output somewhere. Can also be set by TypoScript.
                'simulateStaticDocuments' => 0,                 // Boolean. This is the default value for simulateStaticDocuments (configurable with TypoScript which overrides this, if the TypoScript value is present)
                'noPHPscriptInclude' => 0,                              // Boolean. If set, PHP-scripts are not included by TypoScript configurations, unless they reside in 'media/scripts/'-folder. This is a security option to ensure that users with template-access do not terrorize
+               'strictFormmail' => TRUE,                               // Boolean. If set, the internal "formmail" feature in TYPO3 will send mail ONLY to recipients which has been encoded by the system itself. This protects against spammers misusing the formmailer.
                'compressionLevel' => 0,                                // Determines output compression. Requires zlib in your php4 install. Range 1-9, where 1 is least compression (approx. 50%) and 9 is greatest compression (approx 33%). 'true' as value will set the compression based on system load (works with Linux, freebsd). Good default value is 3. For more info, see class in t3lib/class.gzip_encode.php written by Sandy McArthur, Jr. <Leknor@Leknor.com>
                '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. Integer > 1: Not used yet. Strings: redirect URL, eg. 'notfound.html' or 'http://www.domain.org/errors/notfound.html'.
index 6d27701..84e081d 100755 (executable)
  * 5059:     function HTMLcaseshift($theValue, $case)  
  * 5088:     function bytes($sizeInBytes,$labels)      
  * 5099:     function calcAge($seconds,$labels)        
- * 5130:     function sendNotifyEmail($msg, $recipients, $cc, $email_from, $email_fromName='', $replyTo='')    
+ * 5130:     function sendNotifyEmail($msg, $recipients, $cc, $email_from, $email_fromName='', $replyTo='')
  * 5157:     function URLqMark($url,$params)   
  * 5173:     function checkEmail($email)       
  * 5185:     function clearTSProperties($TSArr,$propList)      
@@ -1677,9 +1677,12 @@ class tslib_cObj {
                                                $fieldCode=$option;
                                        break;
                                        case 'hidden':
-                                               $value=trim($parts[2]);
+                                               $value = trim($parts[2]);
+                                               if (strlen($value) && $confData['fieldname']=='recipient_copy') {
+                                                       $value = $GLOBALS['TSFE']->codeString($value);
+                                               }
                                                $hiddenfields.=sprintf('<input type="hidden" name="%s" value="%s" />',
-                                                       $confData['fieldname'], $value);
+                                                       $confData['fieldname'], htmlspecialchars($value));
                                        break;
                                        case 'property':
                                                if (t3lib_div::inList('type,locationData,goodMess,badMess',$confData['fieldname']))     {
@@ -1775,12 +1778,13 @@ class tslib_cObj {
                        $action=$LD_A['totalURL'];
                }
 
-                       // copyemail:
+                       // Recipient:
                $theEmail = $this->stdWrap($conf['recipient'], $conf['recipient.']);
                if ($theEmail)  {
+                       $theEmail = $GLOBALS['TSFE']->codeString($theEmail);
                        $hiddenfields.='<input type="hidden" name="recipient" value="'.htmlspecialchars($theEmail).'" />';
                }
-               
+
                        // location data:
                if ($conf['locationData'])      {
                        if ($conf['locationData']=='HTTP_POST_VARS' && isset($GLOBALS['HTTP_POST_VARS']['locationData']))       {
@@ -1794,9 +1798,12 @@ class tslib_cObj {
                        // hidden fields:
                if (is_array($conf['hiddenFields.']))   {
                        reset($conf['hiddenFields.']);
-                       while(list($hF_key,$hF_conf)=each($conf['hiddenFields.']))      {
+                       while(list($hF_key,$hF_conf) = each($conf['hiddenFields.']))    {
                                if (substr($hF_key,-1)!='.')    {
                                        $hF_value = $this->cObjGetSingle($hF_conf,$conf['hiddenFields.'][$hF_key.'.'],'hiddenfields');
+                                       if (strlen($value) && $hF_key=='recipient_copy')        {
+                                               $hF_value = $GLOBALS['TSFE']->codeString($hF_value);
+                                       }
                                        $hiddenfields.='<input type="hidden" name="'.$hF_key.'" value="'.htmlspecialchars($hF_value).'" />';
                                }
                        }
index 22651ce..f84d4b9 100755 (executable)
                global $HTTP_POST_VARS;
                
                if ($HTTP_POST_VARS['formtype_db'] || $HTTP_POST_VARS['formtype_mail']) {
-                       $refInfo=parse_url(t3lib_div::getIndpEnv('HTTP_REFERER'));
+                       $refInfo = parse_url(t3lib_div::getIndpEnv('HTTP_REFERER'));
                        if (t3lib_div::getIndpEnv('TYPO3_HOST_ONLY')==$refInfo['host'] || $this->TYPO3_CONF_VARS['SYS']['doNotCheckReferer'])   {
                                if ($this->locDataCheck($HTTP_POST_VARS['locationData']))       {
                                        $ret = '';
                unset($EMAIL_VARS['locationData']);
                unset($EMAIL_VARS['formtype_mail']);
 
+               $integrityCheck = $this->TYPO3_CONF_VARS['FE']['strictFormmail'];
+
+                       // Check recipient field:
+               $encodedFields = explode(',','recipient,recipient_copy');       // These two fields are the ones which contain recipient addresses that can be misused to send mail from foreign servers.
+               foreach($encodedFields as $fieldKey)    {
+                       if (strlen($EMAIL_VARS[$fieldKey]))     {
+                               if ($res = $this->codeString($EMAIL_VARS[$fieldKey], TRUE))     {       // Decode...
+                                       $EMAIL_VARS[$fieldKey] = $res;  // Set value if OK
+                               } elseif ($integrityCheck)      {       // Otherwise abort:
+                                       $GLOBALS['TT']->setTSlogMessage('"Formmail" discovered a field ('.$fieldKey.') which could not be decoded to a valid string. Sending formmail aborted due to security reasons!',3);
+                                       return FALSE;
+                               } else {
+                                       $GLOBALS['TT']->setTSlogMessage('"Formmail" discovered a field ('.$fieldKey.') which could not be decoded to a valid string. The security level accepts this, but you should consider a correct coding though!',2);
+                               }
+                       }
+               }
+
                        // Hook for preprocessing of the content for formmails:
                if (is_array($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass']))    {
                        foreach($this->TYPO3_CONF_VARS['SC_OPTIONS']['tslib/class.tslib_fe.php']['sendFormmail-PreProcClass'] as $_classRef)    {
@@ -2494,13 +2511,63 @@ if (version == "n3") {
                $out = '';
                for ($a=0; $a<strlen($string); $a++)    {
                        $charValue = ord(substr($string,$a,1));
-                       $charValue+= intval($GLOBALS['TSFE']->spamProtectEmailAddresses)*($back?-1:1);
+                       $charValue+= intval($this->spamProtectEmailAddresses)*($back?-1:1);
                        $out.= chr($charValue);
                }
                return $out;
        }
 
        /**
+        * En/decodes strings with lightweight encryption and a hash containing the server encryptionKey (salt)
+        * Can be used for authentication of information sent from server generated pages back to the server to establish that the server generated the page. (Like hidden fields with recipient mail addresses)
+        * Encryption is mainly to avoid spam-bots to pick up information.
+        *
+        * @param       string          Input string to en/decode
+        * @param       boolean         If set, string is decoded, not encoded.
+        * @return      string          encoded/decoded version of $string
+        */
+       function codeString($string, $decode=FALSE)     {
+
+               if ($decode) {
+                       list($md5Hash, $str) = explode(':',$string,2);
+                       $newHash = substr(md5($this->TYPO3_CONF_VARS['SYS']['encryptionKey'].':'.$str),0,10);
+                       if (!strcmp($md5Hash, $newHash))        {
+                               $str = base64_decode($str);
+                               $str = $this->roundTripCryptString($str);
+                               return $str;
+                       } else return FALSE;    // Decoding check failed! Original string not produced by this server!
+               } else {
+                       $str = $string;
+                       $str = $this->roundTripCryptString($str);
+                       $str = base64_encode($str);
+                       $newHash = substr(md5($this->TYPO3_CONF_VARS['SYS']['encryptionKey'].':'.$str),0,10);
+                       return $newHash.':'.$str;
+               }
+       }
+
+       /**
+        * En/decodes strings with lightweight encryption and a hash containing the server encryptionKey (salt)
+        * Can be used for authentication of information sent from server generated pages back to the server to establish that the server generated the page. (Like hidden fields with recipient mail addresses)
+        * Encryption is mainly to avoid spam-bots to pick up information.
+        *
+        * @param       string          Input string to en/decode
+        * @param       boolean         If set, string is decoded, not encoded.
+        * @return      string          encoded/decoded version of $string
+        */
+       function roundTripCryptString($string)  {
+               $out = '';
+               $strLen = strlen($string);
+               $cryptLen = strlen($this->TYPO3_CONF_VARS['SYS']['encryptionKey']);
+
+               for ($a=0; $a < $strLen; $a++)  {
+                       $xorVal = $cryptLen>0 ? ord($this->TYPO3_CONF_VARS['SYS']['encryptionKey']{($a%$cryptLen)}) : 255;
+                       $out.= chr(ord($string{$a}) ^ $xorVal);
+               }
+
+               return $out;
+       }
+
+       /**
         * Checks if a PHPfile may be included.
         *
         * @param       string          Relative path to php file
index 2f9a663..95af46c 100755 (executable)
@@ -178,6 +178,7 @@ class SC_wizard_forms {
        var $doc;                                       // Document template object
        var $content;                           // Content accumulation for the module.
        var $include_once=array();      // List of files to include.
+       var $attachmentCounter = 0;     // Used to numerate attachments automatically.
 
 
                // Internal, static:
@@ -410,7 +411,7 @@ class SC_wizard_forms {
                                
                                        // Special parts:
                                if ($this->special=='formtype_mail' && t3lib_div::inList('formtype_mail,subject,html_enabled',$confData['fieldname']))  {
-                                       $specParts[$confData['fieldname']]=$confData['default'];
+                                       $specParts[$confData['fieldname']] = $confData['default'];
                                } else {
 
                                                // Render title/field preview COLUMN
@@ -452,6 +453,9 @@ class SC_wizard_forms {
                                        $temp_cells=array();
                                        
                                                // Fieldname
+                                       if ($this->special=='formtype_mail' && $confData['type']=='file')       {
+                                               $confData['fieldname'] = 'attachment'.(++$this->attachmentCounter);
+                                       }
                                        $temp_cells[$LANG->getLL('forms_fieldName')]='<input type="text"'.$this->doc->formWidth(10).' name="FORMCFG[c]['.(($k+1)*2).'][fieldname]" value="'.htmlspecialchars($confData['fieldname']).'" title="'.$LANG->getLL('forms_fieldName',1).'" />';
 
                                                // Field configuration depending on the fields type:
@@ -808,8 +812,16 @@ class SC_wizard_forms {
                                                $confData['fieldname'] = str_replace(' ','_',trim($typeParts[0]));
                                        }
                                        
-                                       $confData['default'] = implode(chr(10),t3lib_div::trimExplode(',',$parts[2]));
-       
+                                       switch((string)$confData['type'])       {
+                                               case 'select':
+                                               case 'radio':
+                                                       $confData['default'] = implode(chr(10),t3lib_div::trimExplode(',',$parts[2]));
+                                               break;
+                                               default:
+                                                       $confData['default'] = trim($parts[2]);
+                                               break;
+                                       }
+
                                                // Field configuration depending on the fields type:
                                        switch((string)$confData['type'])       {
                                                case 'textarea':
@@ -842,7 +854,7 @@ class SC_wizard_forms {
                                // Adding config array:
                        $cfgArr[]=$confData;
                }
-               
+
                        // Return cfgArr
                return $cfgArr;
        }