From 537c3e7888c353bf2f8a34be93aecd605eead636 Mon Sep 17 00:00:00 2001 From: garvinhicking Date: Tue, 27 Sep 2005 11:44:18 +0000 Subject: [PATCH] Try to bypass XSRF by using Form tokens. --- docs/NEWS | 3 +- include/admin/category.inc.php | 6 +- include/admin/comments.inc.php | 20 +++-- include/admin/configuration.inc.php | 2 +- include/admin/images.inc.php | 43 +++++++++-- include/admin/import.inc.php | 4 +- include/admin/importers/generic.inc.php | 1 + include/admin/personal.inc.php | 4 +- include/admin/plugins.inc.php | 15 ++-- include/admin/users.inc.php | 8 +- include/functions_config.inc.php | 97 +++++++++++++++++++++++++ include/functions_entries.inc.php | 1 + include/functions_images.inc.php | 1 + include/functions_installer.inc.php | 3 +- 14 files changed, 174 insertions(+), 34 deletions(-) diff --git a/docs/NEWS b/docs/NEWS index 661442e..98c44f5 100644 --- a/docs/NEWS +++ b/docs/NEWS @@ -4,7 +4,8 @@ Version 0.8.5 () ------------------------------------------------------------------------ * More Security: When changing the password in your personal preferences, - you need to insert the old password. Thanks to Nenad Jovanovic for + you need to insert the old password. Secure backend forms with extra + token checks to bypass XSRF attacks. Thanks to Nenad Jovanovic for contacting me about this issue! (garvinhicking) * Fix JS errors in admin comment overview for IE6 (garvinhicking) diff --git a/include/admin/category.inc.php b/include/admin/category.inc.php index bc53c0a..0e12a4f 100644 --- a/include/admin/category.inc.php +++ b/include/admin/category.inc.php @@ -9,7 +9,7 @@ if (IN_serendipity !== true) { $admin_category = ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF ? "AND (authorid = 0 OR authorid = " . (int)$serendipity['authorid'] . ")" : ''); /* Add a new category */ -if (isset($_POST['SAVE'])) { +if (isset($_POST['SAVE']) && serendipity_checkFormToken()) { $name = $serendipity['POST']['cat']['name']; $desc = $serendipity['POST']['cat']['description']; $authorid = (isset($serendipity['POST']['cat']['all_authors']) && $serendipity['POST']['cat']['all_authors'] == 'true') ? 0 : $serendipity['authorid']; @@ -76,7 +76,7 @@ if (isset($_POST['SAVE'])) { } /* Delete a category */ -if ($serendipity['GET']['adminAction'] == 'doDelete') { +if ($serendipity['GET']['adminAction'] == 'doDelete' && serendipity_checkFormToken()) { if ($serendipity['GET']['cid'] != 0) { $remaining_cat = (int)$serendipity['POST']['cat']['remaining_catid']; $category_range = implode(' AND ', serendipity_fetchCategoryRange((int)$serendipity['GET']['cid'])); @@ -118,6 +118,7 @@ if ($serendipity['GET']['adminAction'] == 'doDelete') { if ($serendipity['serendipityUserlevel'] >= USERLEVEL_CHIEF || $serendipity['authorid'] == $this_cat['authorid'] || $this_cat['authorid'] == '0') { ?>
+
: @@ -253,6 +256,7 @@ function highlightComment(id, checkvalue) { } else { ?> +
@@ -349,13 +353,13 @@ foreach ($sql as $rs) {
- <?php echo APPROVE ?> + <?php echo APPROVE ?> strlen($summary) ) { ?> <?php echo VIEW; ?> - <?php echo EDIT; ?> - ")' title="" class="serendipityIconLink"><?php echo DELETE; ?> + <?php echo EDIT; ?> + ")' title="" class="serendipityIconLink"><?php echo DELETE; ?> diff --git a/include/admin/configuration.inc.php b/include/admin/configuration.inc.php index c6953de..00d1750 100644 --- a/include/admin/configuration.inc.php +++ b/include/admin/configuration.inc.php @@ -10,7 +10,7 @@ if (!isset($_POST['installAction'])) { $_POST['installAction'] = ''; } -switch ($_POST['installAction']) { +switch ($_POST['installAction'] && serendipity_checkFormToken()) { case 'check': $oldRewrite = $serendipity['rewrite']; // We save the rewrite method, because we run a check after $serendipity has been updated $res = serendipity_updateConfiguration(); diff --git a/include/admin/images.inc.php b/include/admin/images.inc.php index a9b6dda..de53edc 100644 --- a/include/admin/images.inc.php +++ b/include/admin/images.inc.php @@ -26,6 +26,10 @@ switch ($serendipity['GET']['adminAction']) { break; case 'DoDelete': + if (!serendipity_checkFormToken()) { + break; + } + $file = $serendipity['GET']['fname']; serendipity_deleteImage($serendipity['GET']['fid']); break; @@ -38,7 +42,7 @@ switch ($serendipity['GET']['adminAction']) { } $abortLoc = $serendipity['serendipityHTTPPath'] . 'serendipity_admin.php?serendipity[adminModule]=images'; - $newLoc = $abortLoc . '&serendipity[adminAction]=DoDelete&serendipity[fid]=' . $serendipity['GET']['fid']; + $newLoc = $abortLoc . '&serendipity[adminAction]=DoDelete&serendipity[fid]=' . $serendipity['GET']['fid'] . '&' . serendipity_setFormToken('url'); printf(ABOUT_TO_DELETE_FILE, $file['name'] .'.'. $file['extension']); ?> @@ -56,6 +60,10 @@ switch ($serendipity['GET']['adminAction']) { $file = serendipity_fetchImageFromDatabase($serendipity['GET']['fid']); $serendipity['GET']['newname'] = serendipity_uploadSecure($serendipity['GET']['newname'], true); + if (!serendipity_checkFormToken()) { + return; + } + if ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF && $file['authorid'] != '0' && $file['authorid'] != $serendipity['authorid']) { return; } @@ -108,6 +116,9 @@ switch ($serendipity['GET']['adminAction']) { break; case 'add': + if (!serendipity_checkFormToken()) { + break; + } ?>

@@ -211,6 +222,10 @@ switch ($serendipity['GET']['adminAction']) { case 'directoryDoDelete': + if (!serendipity_checkFormToken()) { + break; + } + if ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF) { return; } @@ -228,6 +243,7 @@ switch ($serendipity['GET']['adminAction']) { } break; + case 'directoryDelete': if ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF) { return; @@ -239,6 +255,7 @@ switch ($serendipity['GET']['adminAction']) {

+ @@ -261,6 +278,10 @@ switch ($serendipity['GET']['adminAction']) { case 'directoryDoCreate': + if (!serendipity_checkFormToken()) { + break; + } + if ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF) { return; } @@ -292,6 +313,7 @@ switch ($serendipity['GET']['adminAction']) {

+
@@ -352,6 +374,7 @@ switch ($serendipity['GET']['adminAction']) {
+ @@ -422,9 +445,9 @@ switch ($serendipity['GET']['adminAction']) { if ( serendipity_rotateImg($serendipity['GET']['fid'], -90) ) { ?> - + - + // location.href="?serendipity[adminModule]=images"; - + + " /> @@ -532,7 +560,7 @@ switch ($serendipity['GET']['adminAction']) { @@ -549,4 +577,3 @@ switch ($serendipity['GET']['adminAction']) { break; } /* vim: set sts=4 ts=4 expandtab : */ -?> diff --git a/include/admin/import.inc.php b/include/admin/import.inc.php index c3d8823..c3f5001 100644 --- a/include/admin/import.inc.php +++ b/include/admin/import.inc.php @@ -92,7 +92,7 @@ class Serendipity_Import { } } -if ( isset($serendipity['GET']['importFrom']) ) { +if (isset($serendipity['GET']['importFrom']) && serendipity_checkFormToken()) { /* Include the importer */ $class = @require_once(S9Y_INCLUDE_PATH . 'include/admin/importers/'. basename($serendipity['GET']['importFrom']) .'.inc.php'); @@ -122,6 +122,7 @@ if ( isset($serendipity['GET']['importFrom']) ) { :

+
getInputFields() as $field ) { ?> @@ -172,6 +173,7 @@ if ( isset($serendipity['GET']['importFrom']) ) {
+ :
@@ -95,13 +96,13 @@ function show_plugins($event_only = false) if ($sort_idx == 0) { $moveup = ' '; } else { - $moveup = '' . UP . ''; + $moveup = '' . UP . ''; } if ($sort_idx == (count($plugins)-1)) { $movedown = ' '; } else { - $movedown = ($moveup != '' ? ' ' : '') . ''. DOWN .''; + $movedown = ($moveup != '' ? ' ' : '') . ''. DOWN .''; } ?> @@ -204,7 +205,7 @@ function placement_box($name, $val, $is_plugin_editable = false) return $x . "\n"; } -if (isset($_GET['serendipity']['plugin_to_move']) && isset($_GET['submit'])) { +if (isset($_GET['serendipity']['plugin_to_move']) && isset($_GET['submit']) && serendipity_checkFormToken()) { if (isset($_GET['serendipity']['event_plugin'])) { $plugins = serendipity_plugin_api::enum_plugins('event', false); } else { @@ -260,7 +261,7 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { $config_names = $bag->get('configuration'); - if (isset($_POST['SAVECONF'])) { + if (isset($_POST['SAVECONF']) && serendipity_checkFormToken()) { /* enum properties and set their values */ foreach ($config_names as $config_item) { @@ -279,6 +280,7 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { +
 
@@ -641,7 +643,7 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { } else { /* show general plugin list */ - if (isset($_POST['SAVE']) && isset($_POST['serendipity']['placement'])) { + if (isset($_POST['SAVE']) && isset($_POST['serendipity']['placement']) && serendipity_checkFormToken()) { foreach ($_POST['serendipity']['placement'] as $plugin_name => $placement) { serendipity_plugin_api::update_plugin_placement( addslashes($plugin_name), @@ -685,7 +687,7 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { } } - if (isset($_POST['REMOVE'])) { + if (isset($_POST['REMOVE']) && serendipity_checkFormToken()) { if (is_array($_POST['serendipity']['plugin_to_remove'])) { foreach ($_POST['serendipity']['plugin_to_remove'] as $key) { $plugin =& serendipity_plugin_api::load_plugin($key); @@ -717,4 +719,3 @@ if (isset($_GET['serendipity']['plugin_to_conf'])) { diff --git a/include/admin/users.inc.php b/include/admin/users.inc.php index a72300b..be26230 100644 --- a/include/admin/users.inc.php +++ b/include/admin/users.inc.php @@ -13,7 +13,7 @@ if ($serendipity['serendipityUserlevel'] < USERLEVEL_CHIEF) { require_once(S9Y_INCLUDE_PATH . 'include/functions_installer.inc.php'); /* Delete a user */ -if (isset($_POST['DELETE_YES'])) { +if (isset($_POST['DELETE_YES']) && serendipity_checkFormToken()) { $user = serendipity_fetchUsers($serendipity['POST']['user']); if ($user[0]['userlevel'] >= $serendipity['serendipityUserlevel'] && $serendipity['serendipityUserlevel'] < USERLEVEL_ADMIN) { echo '
' . CREATE_NOT_AUTHORIZED . '
'; @@ -27,7 +27,7 @@ if (isset($_POST['DELETE_YES'])) { /* Save new user */ -if (isset($_POST['SAVE_NEW'])) { +if (isset($_POST['SAVE_NEW']) && serendipity_checkFormToken()) { if ($_POST['userlevel'] >= $serendipity['serendipityUserlevel'] && $serendipity['serendipityUserlevel'] < USERLEVEL_ADMIN) { echo '
' . CREATE_NOT_AUTHORIZED . '
'; } else { @@ -52,7 +52,7 @@ if (isset($_POST['SAVE_NEW'])) { /* Edit a user */ -if (isset($_POST['SAVE_EDIT'])) { +if (isset($_POST['SAVE_EDIT']) && serendipity_checkFormToken()) { $user = serendipity_fetchUsers($serendipity['POST']['authorid']); if ($user[0]['userlevel'] >= $serendipity['serendipityUserlevel'] && $serendipity['serendipityUserlevel'] < USERLEVEL_ADMIN) { echo '
' . CREATE_NOT_AUTHORIZED . '
'; @@ -136,6 +136,7 @@ if ($serendipity['GET']['adminAction'] == 'edit' || isset($_POST['NEW'])) {

+



+ diff --git a/include/functions_config.inc.php b/include/functions_config.inc.php index 1b3abff..4e90083 100644 --- a/include/functions_config.inc.php +++ b/include/functions_config.inc.php @@ -274,6 +274,7 @@ function serendipity_authenticate_author($username = '', $password = '', $is_md5 $row = serendipity_db_query($query, true, 'assoc'); if (is_array($row)) { + serendipity_setCookie('old_session', session_id()); $_SESSION['serendipityUser'] = $serendipity['serendipityUser'] = $username; $_SESSION['serendipityPassword'] = $serendipity['serendipityPassword'] = $password; $_SESSION['serendipityEmail'] = $serendipity['serendipityEmail'] = $row['email']; @@ -489,5 +490,101 @@ function serendipity_getSessionLanguage() { return $lang; } +function serendipity_checkXSRF() { + global $serendipity; + + // If no module was requested, the user has just logged in and no action will be performed. + if (empty($serendipity['GET']['adminModule'])) { + return false; + } + + // The referrer was empty. Deny access. + if (empty($_SERVER['HTTP_REFERER'])) { + echo serendipity_reportXSRF(1, true, true); + return false; + } + + // Parse the Referrer host. Abort if not parseable. + $hostinfo = @parse_url($_SERVER['HTTP_REFERER']); + if (!is_array($hostinfo)) { + echo serendipity_reportXSRF(2, true, true); + return true; + } + + // Get the server against we will perform the XSRF check. + $server = ''; + if (empty($_SERVER['HTTP_HOST'])) { + $myhost = @parse_url($serendipity['baseURL']); + if (is_array($myhost)) { + $server = $myhost['host']; + } + } else { + $server = $_SERVER['HTTP_HOST']; + } + + // If the current server is different than the referred server, deny access. + if ($hostinfo['host'] != $server) { + echo serendipity_reportXSRF(3, true, true); + return true; + } + + return false; +} + +function serendipity_reportXSRF($type = 0, $reset = true, $use_config = false) { + global $serendipity; + + // Set this in your serendipity_config_local.inc.php if you want HTTP Referrer blocking: + // $serendipity['referrerXSRF'] = true; + + $string = '
' . ERROR_XSRF . '
'; + if ($reset) { + // Config key "referrerXSRF" can be set to enable blocking based on HTTP Referrer. Recommended for Paranoia. + if (($use_config && isset($serendipity['referrerXSRF']) && $serendipity['referrerXSRF']) || $use_config === false) { + $serendipity['GET']['adminModule'] = ''; + } else { + // Paranoia not enabled. Do not report XSRF. + $string = ''; + } + } + return $string; +} + +function serendipity_checkFormToken() { + global $serendipity; + + $token = ''; + if (!empty($serendipity['POST']['token'])) { + $token = $serendipity['POST']['token']; + } elseif (!empty($serendipity['GET']['token'])) { + $token = $serendipity['GET']['token']; + } + + if (empty($token)) { + echo serendipity_reportXSRF('token', false); + return false; + } + + if ($token != md5(session_id()) && + $token != md5($serendipity['COOKIE']['old_session'])) { + echo serendipity_reportXSRF('token', false); + return false; + } + + return true; +} + +function serendipity_setFormToken($type = 'form') { + global $serendipity; + + if ($type == 'form') { + return ''; + } elseif ($type == 'url') { + return 'serendipity[token]=' . md5(session_id()); + } else { + return md5(session_id()); + } +} + /* vim: set sts=4 ts=4 expandtab : */ ?> diff --git a/include/functions_entries.inc.php b/include/functions_entries.inc.php index 3ac9ec0..e914da7 100644 --- a/include/functions_entries.inc.php +++ b/include/functions_entries.inc.php @@ -1207,6 +1207,7 @@ function serendipity_printEntryForm($targetURL, $hiddens = array(), $entry = arr $hidden .= ' ' . $n; $hidden .= ' ' . $n; $hidden .= ' '; + $hidden .= ' ' . serendipity_setFormToken(); if (!empty($errMsg)) { ?> diff --git a/include/functions_images.inc.php b/include/functions_images.inc.php index 18a8a4f..f6b7ce2 100644 --- a/include/functions_images.inc.php +++ b/include/functions_images.inc.php @@ -925,6 +925,7 @@ function serendipity_displayImageList($page = 0, $lineBreak = NULL, $manage = fa ?> $g_val) { if ( !is_array($g_val) && $g_key != 'page' ) { echo ''; diff --git a/include/functions_installer.inc.php b/include/functions_installer.inc.php index f9e5973..8d5b541 100644 --- a/include/functions_installer.inc.php +++ b/include/functions_installer.inc.php @@ -335,6 +335,7 @@ function showConfigAll(count) {
+
1 && $allowToggle) { ?> @@ -448,7 +449,7 @@ function serendipity_parse_sql_tables($filename) { $line = trim(fgets($fp, 4096)); if ($in_table) { $def .= $line; - if (preg_match('/^\)\s*(type\=\S+)?\s*\;$/i', $line)) { + if (preg_match('/^\)\s*(type\=\S+|\{UTF_8\})?\s*\;$/i', $line)) { $in_table = 0; array_push($queries, $def); } -- 2.39.5