[FEATURE] Store passwords encrypted
authorAndreas Wolf <andreas.wolf@ikt-werk.de>
Tue, 24 Jan 2012 22:35:43 +0000 (23:35 +0100)
committerAndreas Wolf <andreas.wolf@ikt-werk.de>
Tue, 24 Jan 2012 22:35:43 +0000 (23:35 +0100)
The password for a WebDAV storage is currently stored unencrypted, which
might pose a security risk if it is exposed e.g. in a database dump.
Therefore, this commit moves the username and password to separate
fields and encrypts the password using Blowfish with Cyclic Block
Chaining (CBC).

Encryption uses a custom wrapper for the mcrypt library. This should be
integrated into the TYPO3 core. There is an existing issue for such a
library, see #21407.

The encrypted password is stored in a custom format including the used
method, encryption mode (CBC, EBC etc) and the initialization vector (if
any).

Resolves: #33303

Classes/Backend/TceMainHook.php [new file with mode: 0644]
Classes/Driver/WebDavDriver.php
Classes/Utility/Encryption.php [new file with mode: 0644]
Configuration/FlexForm/WebDavDriverFlexForm.xml
ext_localconf.php

diff --git a/Classes/Backend/TceMainHook.php b/Classes/Backend/TceMainHook.php
new file mode 100644 (file)
index 0000000..f7cc46d
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * TCEforms integration for the WebDAV driver.
+ *
+ * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
+ */
+class Tx_FalWebdav_Backend_TceMainHook {
+
+       public function processDatamap_preProcessFieldArray(&$incomingFieldArray, $table, $id, $tceMainObject) {
+               if ($table != 'sys_file_storage') {
+                       return;
+               }
+
+               $currentValue = &$incomingFieldArray['configuration']['data']['sDEF']['lDEF']['password']['vDEF'];
+               if (substr($currentValue, 0, 1) == '$') {
+                       return;
+               }
+               $currentValue = Tx_FalWebdav_Utility_Encryption::encryptPassword($currentValue);
+               echo $currentValue;
+       }
+}
index b85dde5..9dc1948 100644 (file)
@@ -24,9 +24,29 @@ class Tx_FalWebdav_Driver_WebDavDriver extends t3lib_file_Driver_AbstractDriver
         */
        protected $davClient;
 
+       /**
+        * The username to use for connecting to the storage.
+        *
+        * @var string
+        */
+       protected $username;
+
+       /**
+        * The password to use for connecting to the storage.
+        *
+        * @var string
+        */
+       protected $password;
+
        public function __construct(array $configuration = array()) {
-               // @†odo Iterate through all string properties and trim them...
+               // TODO Iterate through all string properties and trim them...
                $configuration['baseUrl'] = trim($configuration['baseUrl']);
+               $password = Tx_FalWebdav_Utility_Encryption::decryptPassword($configuration['password']);
+
+                       // TODO check useAuthentication configuration option
+               $this->password = $password;
+               $this->username = $configuration['username'];
+
                parent::__construct($configuration);
        }
 
@@ -50,10 +70,13 @@ class Tx_FalWebdav_Driver_WebDavDriver extends t3lib_file_Driver_AbstractDriver
                }
                $this->basePath = rtrim($urlInfo['path'], '/') . '/';
 
+               $username = $urlInfo['user'] ? $urlInfo['user'] : $this->username;
+               $password = $urlInfo['pass'] ? $urlInfo['pass'] : $this->password;
+
                $settings = array(
                        'baseUri' => $this->configuration['baseUrl'],
-                       'userName' => $urlInfo['user'],
-                       'password' => $urlInfo['pass']
+                       'userName' => $username,
+                       'password' => $password
                );
                unset($urlInfo['user']);
                unset($urlInfo['pass']);
diff --git a/Classes/Utility/Encryption.php b/Classes/Utility/Encryption.php
new file mode 100644 (file)
index 0000000..cf5ceb3
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * Utility methods for encrypting/decrypting data. Currently only supports Blowfish encryption in CBC mode,
+ *
+ * NOTE: This is just a working draft - the methods should be refined and moved to the TYPO3 core.
+ *
+ * @author Andreas Wolf <andreas.wolf@ikt-werk.de>
+ */
+class Tx_FalWebdav_Utility_Encryption {
+
+       protected static $encryptionMethod = MCRYPT_BLOWFISH;
+
+       protected static $encryptionMode = MCRYPT_MODE_CBC;
+
+       public static function getEncryptionMethod() {
+               return self::$encryptionMethod;
+       }
+
+       public static function getEncryptionMode() {
+               return self::$encryptionMode;
+       }
+
+       /**
+        * Returns an initialization vector suitable for the chosen encryption method.
+        *
+        * @return string
+        */
+       protected static function getInitializationVector($encryptionMethod = NULL, $encryptionMode = NULL) {
+               if (!$encryptionMethod) {
+                       $encryptionMethod = self::$encryptionMethod;
+               }
+               if (!$encryptionMode) {
+                       $encryptionMode = self::$encryptionMode;
+               }
+
+               $iv_size = mcrypt_get_iv_size($encryptionMethod, $encryptionMode);
+               $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
+               return $iv;
+       }
+
+       /**
+        * Creates and returns a resource pointer for the encryption method. This is required for e.g. retrieving an
+        * encryption key.
+        *
+        * @return resource
+        */
+       protected static function createEncryptionContext($encryptionMethod = NULL, $encryptionMode = NULL) {
+               if (!$encryptionMethod) {
+                       $encryptionMethod = self::$encryptionMethod;
+               }
+               if (!$encryptionMode) {
+                       $encryptionMode = self::$encryptionMode;
+               }
+
+               $td = mcrypt_module_open($encryptionMethod, '', $encryptionMode, '');
+               return $td;
+       }
+
+       /**
+        * @param $td
+        * @return string
+        */
+       protected static function getEncryptionKey($td) {
+               $ks = mcrypt_enc_get_key_size($td);
+               $key = substr(md5($GLOBALS['TYPO3_CONF_VARS']['SYS']['encryptionKey']), 0, $ks);
+               return $key;
+       }
+
+       /**
+        * Encrypts a password. The algorithm used and - if necessary - the initialization vector are stored within the
+        * password string. The encrypted password itself is stored base64 encoded to make it possible to store this password
+        * in an XML structure.
+        *
+        * @param $value
+        * @return string
+        * @see decryptPassword()
+        */
+       public static function encryptPassword($value) {
+               $td = self::createEncryptionContext();
+               $iv = self::getInitializationVector();
+               $key = self::getEncryptionKey($td);
+               mcrypt_generic_init($td, $key, $iv);
+
+               $encryptedPassword = mcrypt_generic($td, $value);
+               mcrypt_generic_deinit($td, $key, $iv);
+
+               $encryptedText = sprintf('$%s$%s$%s$%s',
+                       self::$encryptionMethod,
+                       self::$encryptionMode,
+                       base64_encode($iv),
+                       base64_encode($encryptedPassword)
+               );
+
+                       // close the module opened for encrypting
+               mcrypt_module_close($td);
+
+               return $encryptedText;
+       }
+
+       /**
+        * Decrypts a password. The necessary initialization vector is extracted from the password.
+        *
+        * @param $encryptedPassword
+        * @return string
+        * @see encryptPassword()
+        */
+       public static function decryptPassword($encryptedPassword) {
+               if (substr_count($encryptedPassword, '$') > 0) {
+                       $passwordParts = t3lib_div::trimExplode('$', $encryptedPassword, TRUE);
+                               // Base64 decoding the password is done below
+                       $encryptedPassword = $passwordParts[3];
+
+                       $encryptionMethod = $passwordParts[0];
+                       $mode = $passwordParts[1];
+                       $td = self::createEncryptionContext($encryptionMethod, $mode);
+
+                       $iv = base64_decode($passwordParts[2]);
+               } else {
+                       $td = self::createEncryptionContext(self::$encryptionMethod, self::$encryptionMode);
+                       $iv = self::getInitializationVector();
+               }
+               $key = self::getEncryptionKey($td);
+
+               mcrypt_generic_init($td, $key, $iv);
+               $decryptedPassword = trim(mdecrypt_generic($td, base64_decode($encryptedPassword)));
+               mcrypt_generic_deinit($td, $key, $iv);
+
+                       // close the module opened for decrypting
+               mcrypt_module_close($td);
+
+               return $decryptedPassword;
+       }
+}
\ No newline at end of file
index 4b19c12..baad644 100644 (file)
                                        <label>WebDAV URL:</label>
                                        <config>
                                                <type>input</type>
-                                               <default>http://user:pass@webdav.domain.com/path/</default>
+                                               <default>http://webdav.domain.com/path/</default>
                                                <eval>trim</eval>
                                                <size>30</size>
                                        </config>
                                </TCEforms>
                        </baseUrl>
+                       <useAuthentication>
+                               <TCEforms>
+                                       <label>Use authentication?</label>
+                                       <config>
+                                               <type>check</type>
+                                       </config>
+                               </TCEforms>
+                       </useAuthentication>
+                       <username>
+                               <TCEforms>
+                                       <label>Username:</label>
+                                       <config>
+                                               <type>input</type>
+                                               <default>username</default>
+                                               <size>30</size>
+                                       </config>
+                               </TCEforms>
+                       </username>
+                       <password>
+                               <TCEforms>
+                                       <label>Password:</label>
+                                       <config>
+                                               <type>input</type>
+                                               <eval>password</eval>
+                                               <size>30</size>
+                                       </config>
+                               </TCEforms>
+                       </password>
                </el>
        </ROOT>
 </T3DataStructure>
index 4fd9b15..d6ad453 100644 (file)
@@ -9,4 +9,6 @@ set_include_path($newPath . PATH_SEPARATOR . get_include_path());
 $registry = t3lib_div::makeInstance('t3lib_file_Driver_DriverRegistry');
 $registry->registerDriverClass('Tx_FalWebdav_Driver_WebDavDriver', 'WebDav', 'WebDAV', 'FILE:EXT:fal_webdav/Configuration/FlexForm/WebDavDriverFlexForm.xml');
 
+$TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'][] = 'EXT:fal_webdav/Classes/Backend/TceMainHook.php:&Tx_FalWebdav_Backend_TceMainHook';
+
 ?>
\ No newline at end of file