From 7e48cf6266338bfdeb11e0283aef6a216d3ff20c Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 14:51:10 +0700 Subject: [PATCH 01/94] csrf_class --- protection/csrf/class_csrf.php | 104 +++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 protection/csrf/class_csrf.php diff --git a/protection/csrf/class_csrf.php b/protection/csrf/class_csrf.php new file mode 100644 index 00000000..90ef70f3 --- /dev/null +++ b/protection/csrf/class_csrf.php @@ -0,0 +1,104 @@ +"; + } + + /** + * Returns true if user-submitted POST token is + * identical to the previously stored SESSION token. + * Returns false otherwise. + */ + public static function isValid() + { + if (isset($_POST['token'])) + { + $user_token = $_POST['token']; + $stored_token = $_SESSION['token']; + return hash_equals($_SESSION['token'], $_POST['token']); + } + else + { + return false; + } + } + + /** + * You can simply check the token validity and + * handle the failure yourself, or you can use + * this "stop-everything-on-failure" method. + */ + public static function exitOnFailure() + { + if (!self::isValid()) + { + exit('Invalid Security Token.'); + } + } + + /** + * This doesn't have to be used but it + * checks to see if the token is recent. + */ + public static function isRecent() + { + if (isset($_SESSION['token_time'])) + { + $stored_time = $_SESSION['token_time']; + return ($stored_time + self::$max_elapsed) >= time(); + } + else + { + self::destroyToken(); + return false; + } + } +} From 0f9050905e53928ac118ae33185cd7185b0b4518 Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 16:12:36 +0700 Subject: [PATCH 02/94] HTMLPurifier.autoload --- .../library/HTMLPurifier.autoload.php | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.autoload.php diff --git a/protection/xss/htmlpurifier/library/HTMLPurifier.autoload.php b/protection/xss/htmlpurifier/library/HTMLPurifier.autoload.php new file mode 100644 index 00000000..d36d9959 --- /dev/null +++ b/protection/xss/htmlpurifier/library/HTMLPurifier.autoload.php @@ -0,0 +1,47 @@ + Date: Wed, 12 Sep 2018 16:13:51 +0700 Subject: [PATCH 03/94] another files in library --- .../library/HTMLPurifier.auto.php | 11 + .../library/HTMLPurifier.autoload-legacy.php | 15 + .../library/HTMLPurifier.composer.php | 4 + .../library/HTMLPurifier.func.php | 25 ++ .../library/HTMLPurifier.includes.php | 234 ++++++++++++++ .../library/HTMLPurifier.kses.php | 30 ++ .../library/HTMLPurifier.path.php | 11 + .../xss/htmlpurifier/library/HTMLPurifier.php | 292 ++++++++++++++++++ .../library/HTMLPurifier.safe-includes.php | 228 ++++++++++++++ 9 files changed, 850 insertions(+) create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.auto.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.autoload-legacy.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.composer.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.func.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.includes.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.kses.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.path.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.php create mode 100644 protection/xss/htmlpurifier/library/HTMLPurifier.safe-includes.php diff --git a/protection/xss/htmlpurifier/library/HTMLPurifier.auto.php b/protection/xss/htmlpurifier/library/HTMLPurifier.auto.php new file mode 100644 index 00000000..c810e87b --- /dev/null +++ b/protection/xss/htmlpurifier/library/HTMLPurifier.auto.php @@ -0,0 +1,11 @@ +purify($html, $config); +} + +// vim: et sw=4 sts=4 diff --git a/protection/xss/htmlpurifier/library/HTMLPurifier.includes.php b/protection/xss/htmlpurifier/library/HTMLPurifier.includes.php new file mode 100644 index 00000000..c3318b3a --- /dev/null +++ b/protection/xss/htmlpurifier/library/HTMLPurifier.includes.php @@ -0,0 +1,234 @@ + $attributes) { + $allowed_elements[$element] = true; + foreach ($attributes as $attribute => $x) { + $allowed_attributes["$element.$attribute"] = true; + } + } + $config->set('HTML.AllowedElements', $allowed_elements); + $config->set('HTML.AllowedAttributes', $allowed_attributes); + if ($allowed_protocols !== null) { + $config->set('URI.AllowedSchemes', $allowed_protocols); + } + $purifier = new HTMLPurifier($config); + return $purifier->purify($string); +} + +// vim: et sw=4 sts=4 diff --git a/protection/xss/htmlpurifier/library/HTMLPurifier.path.php b/protection/xss/htmlpurifier/library/HTMLPurifier.path.php new file mode 100644 index 00000000..353492a1 --- /dev/null +++ b/protection/xss/htmlpurifier/library/HTMLPurifier.path.php @@ -0,0 +1,11 @@ +config = HTMLPurifier_Config::create($config); + $this->strategy = new HTMLPurifier_Strategy_Core(); + } + + /** + * Adds a filter to process the output. First come first serve + * + * @param HTMLPurifier_Filter $filter HTMLPurifier_Filter object + */ + public function addFilter($filter) + { + trigger_error( + 'HTMLPurifier->addFilter() is deprecated, use configuration directives' . + ' in the Filter namespace or Filter.Custom', + E_USER_WARNING + ); + $this->filters[] = $filter; + } + + /** + * Filters an HTML snippet/document to be XSS-free and standards-compliant. + * + * @param string $html String of HTML to purify + * @param HTMLPurifier_Config $config Config object for this operation, + * if omitted, defaults to the config object specified during this + * object's construction. The parameter can also be any type + * that HTMLPurifier_Config::create() supports. + * + * @return string Purified HTML + */ + public function purify($html, $config = null) + { + // :TODO: make the config merge in, instead of replace + $config = $config ? HTMLPurifier_Config::create($config) : $this->config; + + // implementation is partially environment dependant, partially + // configuration dependant + $lexer = HTMLPurifier_Lexer::create($config); + + $context = new HTMLPurifier_Context(); + + // setup HTML generator + $this->generator = new HTMLPurifier_Generator($config, $context); + $context->register('Generator', $this->generator); + + // set up global context variables + if ($config->get('Core.CollectErrors')) { + // may get moved out if other facilities use it + $language_factory = HTMLPurifier_LanguageFactory::instance(); + $language = $language_factory->create($config, $context); + $context->register('Locale', $language); + + $error_collector = new HTMLPurifier_ErrorCollector($context); + $context->register('ErrorCollector', $error_collector); + } + + // setup id_accumulator context, necessary due to the fact that + // AttrValidator can be called from many places + $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); + $context->register('IDAccumulator', $id_accumulator); + + $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context); + + // setup filters + $filter_flags = $config->getBatch('Filter'); + $custom_filters = $filter_flags['Custom']; + unset($filter_flags['Custom']); + $filters = array(); + foreach ($filter_flags as $filter => $flag) { + if (!$flag) { + continue; + } + if (strpos($filter, '.') !== false) { + continue; + } + $class = "HTMLPurifier_Filter_$filter"; + $filters[] = new $class; + } + foreach ($custom_filters as $filter) { + // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat + $filters[] = $filter; + } + $filters = array_merge($filters, $this->filters); + // maybe prepare(), but later + + for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) { + $html = $filters[$i]->preFilter($html, $config, $context); + } + + // purified HTML + $html = + $this->generator->generateFromTokens( + // list of tokens + $this->strategy->execute( + // list of un-purified tokens + $lexer->tokenizeHTML( + // un-purified HTML + $html, + $config, + $context + ), + $config, + $context + ) + ); + + for ($i = $filter_size - 1; $i >= 0; $i--) { + $html = $filters[$i]->postFilter($html, $config, $context); + } + + $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context); + $this->context =& $context; + return $html; + } + + /** + * Filters an array of HTML snippets + * + * @param string[] $array_of_html Array of html snippets + * @param HTMLPurifier_Config $config Optional config object for this operation. + * See HTMLPurifier::purify() for more details. + * + * @return string[] Array of purified HTML + */ + public function purifyArray($array_of_html, $config = null) + { + $context_array = array(); + foreach ($array_of_html as $key => $html) { + $array_of_html[$key] = $this->purify($html, $config); + $context_array[$key] = $this->context; + } + $this->context = $context_array; + return $array_of_html; + } + + /** + * Singleton for enforcing just one HTML Purifier in your system + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier + */ + public static function instance($prototype = null) + { + if (!self::$instance || $prototype) { + if ($prototype instanceof HTMLPurifier) { + self::$instance = $prototype; + } elseif ($prototype) { + self::$instance = new HTMLPurifier($prototype); + } else { + self::$instance = new HTMLPurifier(); + } + } + return self::$instance; + } + + /** + * Singleton for enforcing just one HTML Purifier in your system + * + * @param HTMLPurifier|HTMLPurifier_Config $prototype Optional prototype + * HTMLPurifier instance to overload singleton with, + * or HTMLPurifier_Config instance to configure the + * generated version with. + * + * @return HTMLPurifier + * @note Backwards compatibility, see instance() + */ + public static function getInstance($prototype = null) + { + return HTMLPurifier::instance($prototype); + } +} + +// vim: et sw=4 sts=4 diff --git a/protection/xss/htmlpurifier/library/HTMLPurifier.safe-includes.php b/protection/xss/htmlpurifier/library/HTMLPurifier.safe-includes.php new file mode 100644 index 00000000..852a0b85 --- /dev/null +++ b/protection/xss/htmlpurifier/library/HTMLPurifier.safe-includes.php @@ -0,0 +1,228 @@ + Date: Wed, 12 Sep 2018 17:02:47 +0700 Subject: [PATCH 04/94] protect against xss --- documentation/index.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/documentation/index.php b/documentation/index.php index ffa0a10c..f3fb0dd6 100644 --- a/documentation/index.php +++ b/documentation/index.php @@ -11,13 +11,18 @@ /************************************************************************/ define('TR_INCLUDE_PATH', '../include/'); +define('TR_HTMLPurifier_PATH', '../protection/xss/htmlpurifier/library/'); include(TR_INCLUDE_PATH.'vitals.inc.php'); include(TR_INCLUDE_PATH.'handbook_pages.inc.php'); +require_once(TR_HTMLPurifier_PATH.'HTMLPurifier.auto.php'); + +$config = HTMLPurifier_Config::createDefault(); +$purifier = new HTMLPurifier($config); global $handbook_pages; if (isset($_GET['p'])) { - $p = htmlentities($_GET['p']); + $p = $purifier->purify(htmlentities($_GET['p'])); } else { // go to first handbook page defined in $handbook_pages foreach ($handbook_pages as $page_key => $page_value) From 522d65d305c46535f5ecabc770487adaf076ae3f Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 17:09:09 +0700 Subject: [PATCH 05/94] protect against csrf and xss 1. add paths for protecting against csrf and xss 2. check Token is valid and recent --- profile/index.php | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/profile/index.php b/profile/index.php index c5808a44..0a252408 100644 --- a/profile/index.php +++ b/profile/index.php @@ -11,8 +11,11 @@ /************************************************************************/ define('TR_INCLUDE_PATH', '../include/'); +define('TR_ClassCSRF_PATH', '../protection/csrf/'); +define('TR_HTMLPurifier_PATH', '../protection/xss/htmlpurifier/library/'); require(TR_INCLUDE_PATH.'vitals.inc.php'); require_once(TR_INCLUDE_PATH.'classes/DAO/UsersDAO.class.php'); +require_once(TR_ClassCSRF_PATH.'class_csrf.php'); unset($_SESSION['course_id']); global $_current_user; @@ -32,13 +35,15 @@ } if (isset($_POST['submit'])) { - if (isset($_POST['is_author'])) $is_author = 1; - else $is_author = 0; + if (Token::isValid() AND Token::isRecent()) + { + if (isset($_POST['is_author'])) $is_author = 1; + else $is_author = 0; - $usersDAO = new UsersDAO(); - $user_row = $usersDAO->getUserByID($_SESSION['user_id']); + $usersDAO = new UsersDAO(); + $user_row = $usersDAO->getUserByID($_SESSION['user_id']); - if ($usersDAO->Update($_SESSION['user_id'], + if ($usersDAO->Update($_SESSION['user_id'], $user_row['user_group_id'], $user_row['login'], $user_row['email'], @@ -54,8 +59,12 @@ $_POST['postal_code'], $_POST['status'])) + { + $msg->addFeedback('PROFILE_UPDATED'); + } + } else { - $msg->addFeedback('PROFILE_UPDATED'); + $msg->addError('INVALID_TOKEN'); } } @@ -72,4 +81,4 @@ $onload = 'document.form.first_name.focus();'; $savant->display('profile/index.tmpl.php'); -?> \ No newline at end of file +?> From d94f4beb5265ca9174751b85b10d681e07614419 Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 17:11:43 +0700 Subject: [PATCH 06/94] protect against csrf and xss 1. add paths for protecting csrf and xss 2. check Token is valid and recent --- profile/change_email.php | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/profile/change_email.php b/profile/change_email.php index 497308e2..8a355512 100644 --- a/profile/change_email.php +++ b/profile/change_email.php @@ -11,8 +11,11 @@ /************************************************************************/ define('TR_INCLUDE_PATH', '../include/'); +define('TR_ClassCSRF_PATH', '../protection/csrf/'); +define('TR_HTMLPurifier_PATH', '../protection/xss/htmlpurifier/library/'); require(TR_INCLUDE_PATH.'vitals.inc.php'); require_once(TR_INCLUDE_PATH.'classes/DAO/UsersDAO.class.php'); +require_once(TR_ClassCSRF_PATH.'class_csrf.php'); global $_current_user; @@ -31,10 +34,12 @@ exit; } -if (isset($_POST['submit'])) +if (isset($_POST['submit'])) { - $this_password = $_POST['form_password_hidden']; - + if (Token::isValid() AND Token::isRecent()) + { + $this_password = $_POST['form_password_hidden']; + // password check if (!empty($this_password)) { @@ -55,7 +60,7 @@ header('Location: change_email.php'); exit; } - + // email check if ($_POST['email'] == '') { @@ -78,6 +83,7 @@ if (!$msg->containsErrors()) { + if (defined('TR_EMAIL_CONFIRMATION') && TR_EMAIL_CONFIRMATION) { //send confirmation email @@ -110,6 +116,10 @@ $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY'); } } + } else + { + $msg->addError('INVALID_TOKEN'); + } } $row = $_current_user->getInfo(); @@ -122,4 +132,4 @@ $savant->assign('row', $row); $savant->display('profile/change_email.tmpl.php'); -?> \ No newline at end of file +?> From 50c408bb33be4253ddaef4df87d4ba3013ad1ea9 Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 17:14:39 +0700 Subject: [PATCH 07/94] protect against csrf and xss 1. add paths for protecting against csrf and xss 2. check Token is valid and recent --- profile/change_password.php | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/profile/change_password.php b/profile/change_password.php index 0f5ac868..6a78a600 100644 --- a/profile/change_password.php +++ b/profile/change_password.php @@ -11,7 +11,11 @@ /************************************************************************/ define('TR_INCLUDE_PATH', '../include/'); +define('TR_ClassCSRF_PATH', '../protection/csrf/'); +define('TR_HTMLPurifier_PATH', '../protection/xss/htmlpurifier/library/'); require(TR_INCLUDE_PATH.'vitals.inc.php'); +require_once(TR_ClassCSRF_PATH.'class_csrf.php'); +require_once(TR_HTMLPurifier_PATH.'HTMLPurifier.auto.php'); global $_current_user; @@ -29,12 +33,14 @@ } if (isset($_POST['submit'])) { - if (!empty($_POST['form_old_password_hidden'])) + if (Token::isValid() AND Token::isRecent()) + { + if (!empty($_POST['form_old_password_hidden'])) { //check if old password entered is correct if ($row = $_current_user->getInfo()) { - if ($row['password'] != $_POST['form_old_password_hidden']) + if ($row['password'] != $purifier->purify($_POST['form_old_password_hidden'])) { $msg->addError('WRONG_PASSWORD'); Header('Location: change_password.php'); @@ -64,8 +70,9 @@ } if (!$msg->containsErrors()) { + // insert into the db. - $password = $_POST['form_password_hidden']; + $password = $purifier->purify($_POST['form_password_hidden']); if (!$_current_user->setPassword($password)) { @@ -77,9 +84,13 @@ $msg->addFeedback('PASSWORD_CHANGED'); } + } else + { + $msg->addError('INVALID_TOKEN'); + } } /* template starts here */ $savant->display('profile/change_password.tmpl.php'); -?> \ No newline at end of file +?> From 077d9c3b642f9c7809e36af06387e5fc17403722 Mon Sep 17 00:00:00 2001 From: Metamorfosec <33624021+metamorfosec@users.noreply.github.com> Date: Wed, 12 Sep 2018 17:19:25 +0700 Subject: [PATCH 08/94] protect against xss and csrf 1. start session, add Token 2. autocomplete = "off" --- themes/default/profile/index.tmpl.php | 35 +++++++++++++++++---------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/themes/default/profile/index.tmpl.php b/themes/default/profile/index.tmpl.php index 8fe32d75..8d47ea0f 100644 --- a/themes/default/profile/index.tmpl.php +++ b/themes/default/profile/index.tmpl.php @@ -10,11 +10,19 @@ /* as published by the Free Software Foundation. */ /************************************************************************/ +session_start(); + // show or hide the author information based on the status of the checkbox "author content" global $onload; $onload = "if (jQuery('#is_author').attr('checked')) jQuery('#table_is_author').show(); else jQuery('#table_is_author').hide();"; require(TR_INCLUDE_PATH.'header.inc.php'); +require_once(TR_ClassCSRF_PATH.'class_csrf.php'); +require_once(TR_HTMLPurifier_PATH.'HTMLPurifier.auto.php'); + +$config = HTMLPurifier_Config::createDefault(); +$purifier = new HTMLPurifier($config); + //Timer $mtime = microtime(); $mtime = explode(' ', $mtime); @@ -22,7 +30,7 @@ $starttime = $mtime; ?> -