From 349b394ed89b8c7be35c57884eb9c13f8ca5f5e9 Mon Sep 17 00:00:00 2001 From: Chris Burgess Date: Thu, 30 Jan 2014 23:42:28 +1300 Subject: [PATCH] CRM-14091. Look for obvious misconfigurations and warn. --- CRM/Core/Page.php | 1 + CRM/Utils/Check/Security.php | 231 +++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 CRM/Utils/Check/Security.php diff --git a/CRM/Core/Page.php b/CRM/Core/Page.php index 95ff557578..4fc023cac4 100644 --- a/CRM/Core/Page.php +++ b/CRM/Core/Page.php @@ -195,6 +195,7 @@ class CRM_Core_Page { if (empty($_GET['snippet'])) { // Version check and intermittent alert to admins CRM_Utils_VersionCheck::singleton()->versionAlert(); + CRM_Utils_Check_Security::singleton()->allChecks(); // Debug msg once per hour if ($config->debug && CRM_Core_Permission::check('administer CiviCRM') && CRM_Core_Session::singleton()->timer('debug_alert', 3600)) { diff --git a/CRM/Utils/Check/Security.php b/CRM/Utils/Check/Security.php new file mode 100644 index 0000000000..ab554cf4ab --- /dev/null +++ b/CRM/Utils/Check/Security.php @@ -0,0 +1,231 @@ +CheckLogFileIsNotAccessible(); + CRM_Utils_Check_Security::singleton()->CheckUploadsAreNotAccessible(); + CRM_Utils_Check_Security::singleton()->CheckDirectoriesAreNotBrowseable(); + } + } + + /** + * Check if our logfile is directly accessible. + * + * Per CiviCRM default the logfile sits in a folder which is + * web-accessible, and is protected by a default .htaccess + * configuration. If server config causes the .htaccess not to + * function as intended, there may be information disclosure. + * + * The debug log may be jam-packed with sensitive data, we don't + * want that. + * + * Being able to be retrieved directly doesn't mean the logfile + * is browseable or visible to search engines; it means it can be + * requested directly. + * + * @see CRM-14091 + */ + public function CheckLogFileIsNotAccessible() { + $config = CRM_Core_Config::singleton(); + + $log = CRM_Core_Error::createDebugLogger(); + $log_filename = $log->_filename; + + // Hazard a guess at the URL of the logfile, based on common + // CiviCRM layouts. + switch ($config->userFramework) { + // If other frameworks lay out differently, add them here. + + // Drupal style - look for '/files/' and stitch the known paths + // (based on CIVICRM_TEMPLATE_COMPILEDIR and $config->uploadDir) + // together. + case 'Drupal': + case 'Drupal6': + default: + if ($upload_url = explode('/files/', $config->imageUploadURL)) { + $url[] = $upload_url[0]; + if ($log_path = explode('/files/', $log_filename)) { + $url[] = $log_path[1]; + $log_url = implode('/files/', $url); + // Fake a log being internet-accessible. + // $log_url = 'https://gist.github.com/xurizaemon/2141ee4e042c273c8979/raw/3eda5da63b114e206c2516569f88a45305cb1469/CiviCRM.aabbccdd.log'; + $docs_url = 'http://wiki.civicrm.org/confluence/display/CRMDOC/Security/LogNotAccessible'; + if ($log = @file_get_contents($log_url)) { + $msg = 'The CiviCRM debug log should not be downloadable.' + . '
' . + 'Read more about this warning'; + $msg = ts($msg, array(1 => $log_url, 2 => $docs_url)); + CRM_Core_Session::setStatus($msg, ts('Security Warning')); + } + } + } + } + } + + /** + * Check if our uploads directory has accessible files. + * + * We'll test a handful of files randomly. Hazard a guess at the URL + * of the uploads dir, based on common CiviCRM layouts. Try and + * request the files, and if any are successfully retrieved, warn. + * + * Being retrievable doesn't mean the files are browseable or visible + * to search engines; it only means they can be requested directly. + * + * @see CRM-14091 + */ + public function CheckUploadsAreNotAccessible() { + $config = CRM_Core_Config::singleton(); + // @TODO: Test with WordPress, Joomla. + switch ($config->userFramework) { + // Drupal style - look for '/files/' and stitch the known paths + // (based on CIVICRM_TEMPLATE_COMPILEDIR and $config->uploadDir) + // together. + case 'Drupal': + case 'Drupal6': + default: + if ($upload_url = explode('/files/', $config->imageUploadURL)) { + if ($files = glob($config->uploadDir . '/*')) { + for ($i=0; $i<3; $i++) { + $f = array_rand($files); + if ($file_path = explode('/files/', $files[$f])) { + $url = implode('/files/', array($upload_url[0], $file_path[1])); + if ($file = @file_get_contents($url)) { + $msg = 'Files in the upload directory should not be downloadable.' + . '
' . + 'Read more about this warning'; + $docs_url = 'http://wiki.civicrm.org/confluence/display/CRMDOC/Security/UploadDirNotAccessible'; + $msg = ts($msg, array(1 => $docs_url)); + CRM_Core_Session::setStatus($msg, ts('Security Warning')); + } + } + } + } + } + } + } + + /** + * Check if our uploads or ConfigAndLog directories have browseable + * listings. + * + * Retrieve a listing of files from the local filesystem, and the + * corresponding path via HTTP. Then check and see if the local + * files are represented in the HTTP result; if so then warn. This + * MAY trigger false positives (if you have files named 'a', 'e' + * we'll probably match that). + * + * @see CRM-14091 + */ + public function CheckDirectoriesAreNotBrowseable() { + $config = CRM_Core_Config::singleton(); + $log = CRM_Core_Error::createDebugLogger(); + $log_name = $log->_filename; + + // @TODO: Test with WordPress, Joomla. + switch ($config->userFramework) { + // Drupal style - look for '/files/' and stitch the known paths + // (based on CIVICRM_TEMPLATE_COMPILEDIR and URL settings) + // together. + case 'Drupal': + case 'Drupal6': + default: + $paths = array( + $config->uploadDir, + dirname($log_name), + ); + if ($upload_url = explode('/files/', $config->imageUploadURL)) { + if ($files = glob($config->uploadDir . '/*')) { + foreach ($paths as $path) { + if ($dir_path = explode('/files/', $path)) { + $url = implode('/files/', array($upload_url[0], $dir_path[1])); + if ($files = glob($path . '/*')) { + if ($listing = @file_get_contents($url)) { + foreach ($files as $file) { + if (stristr($listing, $file)) { + $msg = 'Directory %2 may be browseable via the web.' + . '
' . + 'Read more about this warning'; + $docs_url = 'http://wiki.civicrm.org/confluence/display/CRMDOC/Security/UploadDirNotAccessible'; + $msg = ts($msg, array(1 => $log_url, 2 => $path, 3 => $docs_url)); + CRM_Core_Session::setStatus($msg, ts('Security Warning')); + } + } + } + } + } + } + } + } + } + } + +} -- 2.25.1