CVE-2013-5029: Improper Input Validation (CWE-20)
value: phpMyAdmin 3.5.x and 4.0.x before 4.0.5 allows remote attackers to bypass the clickjacking protection mechanism via certain vectors related to Header.class.php.
CWE description: The product receives input or data, but it does
        not validate or incorrectly validates that the input has the
        properties that are required to process the data safely and
        correctly.
Published: 2013-08-19T23:55Z
Repository: https://github.com/phpmyadmin/phpmyadmin
Language: PHP
Files changed: doc/config.rst, js/cross_framing_protection.js, libraries/Header.class.php, libraries/Scripts.class.php, libraries/config.default.php, libraries/config/messages.inc.php, libraries/config/setup.forms.php
Commit date: 2013-07-29 14:55:22 +0200
Commit message: Readd AllowThirdPartyFraming option

We want by default no framing of phpMyAdmin, but still some sites might
need to embed it, so configuration option (with appropriate security
warning) is there.

This basically reverts d7e0bed28443e6cf8f41965cc2f27be19c667fa6.

Diff: diff --git a/doc/config.rst b/doc/config.rst
index 6099d80c3d6c..6a5d7cf813f8 100644
--- a/doc/config.rst
+++ b/doc/config.rst
@@ -117,10 +117,19 @@ Basic settings
     :type: integer
     :default: 80
 
     Show warning about incomplete translations on certain threshold.
 
+.. config:option:: $cfg['AllowThirdPartyFraming']
+
+    :type: boolean
+    :default: false
+    
+    Setting this to ``true`` allows phpMyAdmin to be included inside a frame,
+    and is a potential security hole allowing cross-frame scripting attacks or
+    clickjacking.
+
 Server connection settings
 --------------------------
 
 .. config:option:: $cfg['Servers']
 
diff --git a/js/cross_framing_protection.js b/js/cross_framing_protection.js
new file mode 100644
index 000000000000..6fb780350fca
--- /dev/null
+++ b/js/cross_framing_protection.js
@@ -0,0 +1,14 @@
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Conditionally included if third-party framing is not allowed
+ *
+ */
+
+try {
+    if (top != self) {
+        top.location.href = self.location.href;
+    }
+} catch(e) {
+    alert("Redirecting... (error: " + e);
+    top.location.href = self.location.href;
+}
diff --git a/libraries/Header.class.php b/libraries/Header.class.php
index 1392bc3e3d0d..810d556904cc 100644
--- a/libraries/Header.class.php
+++ b/libraries/Header.class.php
@@ -156,10 +156,16 @@ private function _addDefaultScripts()
         $this->_scripts->addFile('jquery/jquery.event.drag-2.2.js');
         $this->_scripts->addFile('jquery/jquery-ui-timepicker-addon.js');
         $this->_scripts->addFile('jquery/jquery.ba-hashchange-1.3.js');
         $this->_scripts->addFile('jquery/jquery.debounce-1.0.5.js');
         $this->_scripts->addFile('jquery/jquery.menuResizer-1.0.js');
+
+        // Cross-framing protection
+        if ($GLOBALS['cfg']['AllowThirdPartyFraming'] === false) {
+            $this->_scripts->addFile('cross_framing_protection.js');
+        }
+
         $this->_scripts->addFile('rte.js');
 
         // Here would not be a good place to add CodeMirror because
         // the user preferences have not been merged at this point
 
@@ -447,10 +453,16 @@ public function sendHttpHeaders()
         /**
          * Sends http headers
          */
         $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT';
         if (! defined('TESTSUITE')) {
+            /* Prevent against ClickJacking by disabling framing */
+            if (! $GLOBALS['cfg']['AllowThirdPartyFraming']) {
+                header(
+                    'X-Frame-Options: DENY'
+                );
+            }
             header(
                 "X-Content-Security-Policy: default-src 'self' "
                 . $GLOBALS['cfg']['CSPAllow'] . ';'
                 . "options inline-script eval-script;"
                 . "img-src 'self' data: "
diff --git a/libraries/Scripts.class.php b/libraries/Scripts.class.php
index c0d64d8ab15d..a3057d7e0b24 100644
--- a/libraries/Scripts.class.php
+++ b/libraries/Scripts.class.php
@@ -127,10 +127,11 @@ private function _eventBlacklist($filename)
             || strpos($filename, 'codemirror') !== false
             || strpos($filename, 'messages.php') !== false
             || strpos($filename, 'ajax.js') !== false
             || strpos($filename, 'navigation.js') !== false
             || strpos($filename, 'get_image.js.php') !== false
+            || strpos($filename, 'cross_framing_protection.js') !== false
         ) {
             return 0;
         } else {
             return 1;
         }
diff --git a/libraries/config.default.php b/libraries/config.default.php
index 598573c508da..d164d768e90f 100644
--- a/libraries/config.default.php
+++ b/libraries/config.default.php
@@ -81,10 +81,18 @@
  *
  * @global boolean $cfg['TranslationWarningThreshold']
  */
 $cfg['TranslationWarningThreshold'] = 80;
 
+/**
+ * Allows phpMyAdmin to be included from a other document in a frame;
+ * setting this to true is a potential security hole
+ *
+ * @global boolean $cfg['AllowThirdPartyFraming']
+ */
+$cfg['AllowThirdPartyFraming'] = false;
+
 /**
  * The 'cookie' auth_type uses blowfish algorithm to encrypt the password. If
  * at least one server configuration uses 'cookie' auth_type, enter here a
  * pass phrase that will be used by blowfish. The maximum length seems to be 46
  * characters.
diff --git a/libraries/config/messages.inc.php b/libraries/config/messages.inc.php
index 42d72835cc91..350bb0df0fd9 100644
--- a/libraries/config/messages.inc.php
+++ b/libraries/config/messages.inc.php
@@ -14,10 +14,12 @@
     PMA_fatalError('Bad invocation!');
 }
 
 $strConfigAllowArbitraryServer_desc = __('If enabled user can enter any MySQL server in login form for cookie auth');
 $strConfigAllowArbitraryServer_name = __('Allow login to any MySQL server');
+$strConfigAllowThirdPartyFraming_desc = __('Enabling this allows a page located on a different domain to call phpMyAdmin inside a frame, and is a potential [strong]security hole[/strong] allowing cross-frame scripting attacks');
+$strConfigAllowThirdPartyFraming_name = __('Allow third party framing');
 $strConfigAllowUserDropDatabase_name = __('Show &quot;Drop database&quot; link to normal users');
 $strConfigblowfish_secret_desc = __('Secret passphrase used for encrypting cookies in [kbd]cookie[/kbd] authentication');
 $strConfigblowfish_secret_name = __('Blowfish secret');
 $strConfigBrowseMarkerEnable_desc = __('Highlight selected rows');
 $strConfigBrowseMarkerEnable_name = __('Row marker');
diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php
index bb712cabca7f..f172462d7d79 100644
--- a/libraries/config/setup.forms.php
+++ b/libraries/config/setup.forms.php
@@ -134,11 +134,12 @@
     'PersistentConnections',
     'ExecTimeLimit',
     'MemoryLimit',
     'SkipLockedTables',
     'DisableMultiTableMaintenance',
-    'UseDbSearch');
+    'UseDbSearch',
+    'AllowThirdPartyFraming');
 $forms['Sql_queries']['Sql_queries'] = array(
     'ShowSQL',
     'Confirm',
     'QueryHistoryDB',
     'QueryHistoryMax',

Vulnerable code:     'UseDbSearch');
Fixed code: .. config:option:: $cfg['AllowThirdPartyFraming']

    :type: boolean
    :default: false
    
    Setting this to ``true`` allows phpMyAdmin to be included inside a frame,
    and is a potential security hole allowing cross-frame scripting attacks or
    clickjacking.

/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
 * Conditionally included if third-party framing is not allowed
 *
 */

try {
    if (top != self) {
        top.location.href = self.location.href;
    }
} catch(e) {
    alert("Redirecting... (error: " + e);
    top.location.href = self.location.href;
}

        // Cross-framing protection
        if ($GLOBALS['cfg']['AllowThirdPartyFraming'] === false) {
            $this->_scripts->addFile('cross_framing_protection.js');
        }

            /* Prevent against ClickJacking by disabling framing */
            if (! $GLOBALS['cfg']['AllowThirdPartyFraming']) {
                header(
                    'X-Frame-Options: DENY'
                );
            }
            || strpos($filename, 'cross_framing_protection.js') !== false
/**
 * Allows phpMyAdmin to be included from a other document in a frame;
 * setting this to true is a potential security hole
 *
 * @global boolean $cfg['AllowThirdPartyFraming']
 */
$cfg['AllowThirdPartyFraming'] = false;

$strConfigAllowThirdPartyFraming_desc = __('Enabling this allows a page located on a different domain to call phpMyAdmin inside a frame, and is a potential [strong]security hole[/strong] allowing cross-frame scripting attacks');
$strConfigAllowThirdPartyFraming_name = __('Allow third party framing');
    'UseDbSearch',
    'AllowThirdPartyFraming');