- Stop using deprecated ereg functions. (#2820952)
- Remove personal data from Message ID seed. (#880029/847107)
- Implemented page referal verification mechanism. (Secunia Advisory SA34627)
+ - Implemented security token system. (Secunia Advisory SA34627)
Version 1.5.1 (branched on 2006-02-12)
--------------------------------------
global $oTemplate;
- $output = addForm($form_url, 'post', 'f_add');
+ $output = addForm($form_url, 'post', 'f_add', '', '', array(), TRUE);
if ($button == _("Update address")) {
$edit = true;
/**
* Make a <form> start-tag.
*
- * @param string $sAction form handler URL
- * @param string $sMethod http method used to submit form data. 'get' or 'post'
- * @param string $sName form name used for identification (used for backward
- * compatibility). Use of id is recommended instead.
- * @param string $sEnctype content type that is used to submit data. html 4.01
- * defaults to 'application/x-www-form-urlencoded'. Form
- * with file field needs 'multipart/form-data' encoding type.
- * @param string $sCharset charset that is used for submitted data
- * @param array $aAttribs (since 1.5.1) extra attributes
+ * @param string $sAction form handler URL
+ * @param string $sMethod http method used to submit form data. 'get' or 'post'
+ * @param string $sName form name used for identification (used for backward
+ * compatibility). Use of id is recommended instead.
+ * @param string $sEnctype content type that is used to submit data. html 4.01
+ * defaults to 'application/x-www-form-urlencoded'. Form
+ * with file field needs 'multipart/form-data' encoding type.
+ * @param string $sCharset charset that is used for submitted data
+ * @param array $aAttribs (since 1.5.1) extra attributes
+ * @param boolean $bAddToken (since 1.5.2) When given as a string or as boolean TRUE,
+ * a hidden input is also added to the form containing a
+ * security token. When given as TRUE, the input name is
+ * "smtoken"; otherwise the name is the string that is
+ * given for this parameter. When FALSE, no hidden token
+ * input field is added. (OPTIONAL; default not used)
*
* @return string html formated form start string
*
*/
-function addForm($sAction, $sMethod = 'post', $sName = '', $sEnctype = '', $sCharset = '', $aAttribs = array()) {
+function addForm($sAction, $sMethod = 'post', $sName = '', $sEnctype = '', $sCharset = '', $aAttribs = array(), $bAddToken = FALSE) {
global $oTemplate;
$oTemplate->assign('enctype', $sEnctype);
$oTemplate->assign('charset', $sCharset);
- return $oTemplate->fetch('form.tpl');
+ $sForm = $oTemplate->fetch('form.tpl');
+
+ if ($bAddToken) {
+ $sForm .= addHidden((is_string($bAddToken) ? $bAddToken : 'smtoken'),
+ sm_generate_security_token());
+ }
+
+ return $sForm;
}
/**
$aUid = (isset($msg) && is_array($msg)) ? array_values($msg) : $aUid;
if (count($aUid) && $sButton != 'expunge') {
+ // don't do anything to any messages until we have done security check
+ // FIXME: not sure this code really belongs here, but there's nowhere else to put it with this architecture
+ // FIXME: we might need to open this up to SQ_FORM instead, especially for plugins (?)
+ sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
// make sure message UIDs are sanitized (BIGINT)
foreach ($aUid as $i => $uid)
$aUid[$i] = (preg_match('/^[0-9]+$/', $uid) ? $uid : '0');
function sq_trim_value ( &$value ) {
$value = trim($value);
}
+
+/**
+ * Gathers the list of secuirty tokens currently
+ * stored in the user's preferences and optionally
+ * purges old ones from the list.
+ *
+ * @param boolean $purge_old Indicates if old tokens
+ * should be purged from the
+ * list ("old" is 30 days or
+ * older unless the administrator
+ * overrides that value using
+ * $max_security_token_age in
+ * config/config_local.php)
+ * (OPTIONAL; default is to always
+ * purge old tokens)
+ *
+ * @return array The list of tokens
+ *
+ * @since 1.4.19 and 1.5.2
+ *
+ */
+function sm_get_user_security_tokens($purge_old=TRUE)
+{
+
+ global $data_dir, $username, $max_token_age_days;
+
+ $tokens = getPref($data_dir, $username, 'security_tokens', '');
+ if (($tokens = unserialize($tokens)) === FALSE || !is_array($tokens))
+ $tokens = array();
+
+ // purge old tokens if necessary
+ //
+ if ($purge_old)
+ {
+ if (empty($max_token_age_days)) $max_token_age_days = 30;
+ $now = time();
+ $discard_token_date = $now - ($max_token_age_days * 86400);
+ $cleaned_tokens = array();
+ foreach ($tokens as $token => $timestamp)
+ if ($timestamp >= $discard_token_date)
+ $cleaned_tokens[$token] = $timestamp;
+ $tokens = $cleaned_tokens;
+ }
+
+ return $tokens;
+
+}
+
+/**
+ * Generates a security token that is then stored in
+ * the user's preferences with a timestamp for later
+ * verification/use.
+ *
+ * WARNING: If the administrator has turned the token system
+ * off by setting $disable_security_tokens to TRUE in
+ * config/config_local.php, this function will not
+ * store tokens in the user preferences (but it will
+ * still generate and return a random string).
+ *
+ * @return void
+ *
+ * @since 1.4.19 and 1.5.2
+ *
+ */
+function sm_generate_security_token()
+{
+
+ global $data_dir, $username, $disable_security_tokens;
+ $max_generation_tries = 1000;
+
+ $tokens = sm_get_user_security_tokens();
+
+ $new_token = GenerateRandomString(12, '', 7);
+ $count = 0;
+ while (isset($tokens[$new_token]))
+ {
+ $new_token = GenerateRandomString(12, '', 7);
+ if (++$count > $max_generation_tries)
+ {
+ logout_error(_("Fatal token generation error; please contact your system administrator or the SquirrelMail Team"));
+ exit;
+ }
+ }
+
+ // is the token system enabled? CAREFUL!
+ //
+ if (!$disable_security_tokens)
+ {
+ $tokens[$new_token] = time();
+ setPref($data_dir, $username, 'security_tokens', serialize($tokens));
+ }
+
+ return $new_token;
+
+}
+
+/**
+ * Validates a given security token and optionally remove it
+ * from the user's preferences if it was valid. If the token
+ * is too old but otherwise valid, it will still be rejected.
+ *
+ * "Too old" is 30 days or older unless the administrator
+ * overrides that value using $max_security_token_age in
+ * config/config_local.php
+ *
+ * WARNING: If the administrator has turned the token system
+ * off by setting $disable_security_tokens to TRUE in
+ * config/config_local.php, this function will always
+ * return TRUE.
+ *
+ * @param string $token The token to validate
+ * @param int $validity_period The number of seconds tokens are valid
+ * for (set to zero to remove valid tokens
+ * after only one use; use 3600 to allow
+ * tokens to be reused for an hour)
+ * (OPTIONAL; default is to only allow tokens
+ * to be used once)
+ * @param boolean $show_error Indicates that if the token is not
+ * valid, this function should display
+ * a generic error, log the user out
+ * and exit - this function will never
+ * return in that case.
+ * (OPTIONAL; default FALSE)
+ *
+ * @return boolean TRUE if the token validated; FALSE otherwise
+ *
+ * @since 1.4.19 and 1.5.2
+ *
+ */
+function sm_validate_security_token($token, $validity_period=0, $show_error=FALSE)
+{
+
+ global $data_dir, $username, $max_token_age_days,
+ $disable_security_tokens;
+
+ // bypass token validation? CAREFUL!
+ //
+ if ($disable_security_tokens) return TRUE;
+
+ // don't purge old tokens here because we already
+ // do it when generating tokens
+ //
+ $tokens = sm_get_user_security_tokens(FALSE);
+
+ // token not found?
+ //
+ if (empty($tokens[$token]))
+ {
+ if (!$show_error) return FALSE;
+ logout_error(_("This page request could not be verified and appears to have expired."));
+ exit;
+ }
+
+ $now = time();
+ $timestamp = $tokens[$token];
+
+ // whether valid or not, we want to remove it from
+ // user prefs if it's old enough
+ //
+ if ($timestamp < $now - $validity_period)
+ {
+ unset($tokens[$token]);
+ setPref($data_dir, $username, 'security_tokens', serialize($tokens));
+ }
+
+ // reject tokens that are too old
+ //
+ if (empty($max_token_age_days)) $max_token_age_days = 30;
+ $old_token_date = $now - ($max_token_age_days * 86400);
+ if ($timestamp < $old_token_date)
+ {
+ if (!$show_error) return FALSE;
+ logout_error(_("The current page request appears to have originated from an untrusted source."));
+ exit;
+ }
+
+ // token OK!
+ //
+ return TRUE;
+
+}
+
global $PHP_SELF, $oTemplate, $oErrorHandler;
- echo addForm($PHP_SELF, 'post', 'addressbook').
+//FIXME: no HTML output from core
+ echo addForm($PHP_SELF, 'post', 'addressbook', '', '', array(), TRUE).
addHidden('html_addr_search_done', 'true');
addr_insert_hidden();
if ($addrquery == '' || sizeof($res) == 0) {
//FIXME don't echo HTML from core -- especially convoluted given that there is template code immediately above AND below this block
echo '<div style="text-align: center;">'.
- addForm('compose.php','post','k');
+ addForm('compose.php','post','k', '', '', array(), TRUE);
addr_insert_hidden();
echo '<input type="submit" value="' . _("Return") . '" name="return" />' . "\n" .
'</form></div></nobr>';
/** lets get the global vars we may need */
/* From the address form */
+sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
sqgetGlobalVar('addaddr', $addaddr, SQ_POST);
sqgetGlobalVar('editaddr', $editaddr, SQ_POST);
sqgetGlobalVar('deladdr', $deladdr, SQ_POST);
/* Handle user's actions */
if(sqgetGlobalVar('REQUEST_METHOD', $req_method, SQ_SERVER) && $req_method == 'POST') {
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
/**************************************************
* Add new address *
**************************************************/
if ( sqgetGlobalVar('smaction_edit_new',$tmp) ) $action = 'edit_as_new';
}
+sqgetGlobalVar('smtoken', $submitted_token, $SQ_GLOBAL, '');
+
/**
* Here we decode the data passed in from mailto.php.
*/
}
if ($draft) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
/*
* Set $default_charset to correspond with the user's selection
* of language interface.
}
if ($send) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
if (isset($_FILES['attachfile']) &&
$_FILES['attachfile']['tmp_name'] &&
$_FILES['attachfile']['tmp_name'] != 'none') {
/* sqimap_logout($imapConnection); */
}
} elseif (isset($html_addr_search_done)) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
if ($compose_new_win == '1') {
compose_Header($color, $mailbox);
}
*/
include_once('./addrbook_search_html.php');
} elseif (isset($attach)) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
if ($compose_new_win == '1') {
compose_Header($color, $mailbox);
} else {
showInputForm($session);
}
elseif (isset($sigappend)) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
$signature = $idents[$identity]['signature'];
$body .= "\n\n".($prefix_sig==true? "-- \n":'').$signature;
}
showInputForm($session);
} elseif (isset($do_delete)) {
+
+ // validate security token
+ //
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
if ($compose_new_win == '1') {
compose_Header($color, $mailbox);
} else {
//FIXME: NO HTML IN CORE!
echo ">\n";
+//FIXME: DON'T ECHO HTML FROM CORE!
+ echo addHidden('smtoken', sm_generate_security_token());
+
//FIXME: DON'T ECHO HTML FROM CORE!
echo addHidden('startMessage', $startMessage);
/* get globals we may need */
sqgetGlobalVar('delimiter', $delimiter, SQ_SESSION);
sqgetGlobalVar('smaction', $action, SQ_POST);
+sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
/* end of get globals */
switch ($action)
{
case 'create':
+
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
sqgetGlobalVar('folder_name', $folder_name, SQ_POST);
sqgetGlobalVar('subfolder', $subfolder, SQ_POST);
sqgetGlobalVar('contain_subs', $contain_subs, SQ_POST);
sqgetGlobalVar('old_name', $old_name, SQ_POST);
folders_rename_getname($imapConnection, $delimiter, $old_name);
} else {
+
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
sqgetGlobalVar('orig', $orig, SQ_POST);
sqgetGlobalVar('old_name', $old_name, SQ_POST);
folders_rename_do($imapConnection, $delimiter, $orig, $old_name, $new_name);
}
sqgetGlobalVar('folder_name', $folder_name, SQ_POST);
if ( sqgetGlobalVar('confirmed', $dummy, SQ_POST) ) {
+
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
folders_delete_do($imapConnection, $delimiter, $folder_name);
$td_str = _("Deleted folder successfully.");
} else {
}
break;
case 'subscribe':
+
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
sqgetGlobalVar('folder_names', $folder_names, SQ_POST);
folders_subscribe($imapConnection, $folder_names);
$td_str = _("Subscribed successfully.");
break;
case 'unsubscribe':
+
+ // first, validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
sqgetGlobalVar('folder_names', $folder_names, SQ_POST);
folders_unsubscribe($imapConnection, $folder_names);
$td_str = _("Unsubscribed successfully.");
/* get the globals that we may need */
sqgetGlobalVar('optpage', $optpage);
-sqgetGlobalVar('optmode', $optmode, SQ_FORM);
-sqgetGlobalVar('optpage_data',$optpage_data, SQ_POST);
+sqgetGlobalVar('optmode', $optmode, SQ_FORM);
+sqgetGlobalVar('optpage_data',$optpage_data, SQ_POST);
+sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
/* end of getting globals */
/* Make sure we have an Option Page set. Default to main. */
/*** Next, process anything that needs to be processed. ***/
/***********************************************************/
+// security check before saving anything...
+//FIXME: what about SMOPT_MODE_LINK??
+if ($optmode == SMOPT_MODE_SUBMIT) {
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+}
+
$optpage_save_error=array();
if ( isset( $optpage_data ) ) {
}
// Begin output form
- echo addForm('options.php', 'post', 'option_form')
+ echo addForm('options.php', 'post', 'option_form', '', '', array(), TRUE)
. create_optpage_element($optpage)
. create_optmode_element(SMOPT_MODE_SUBMIT);
sqGetGlobalVar('color_type', $color_type);
sqGetGlobalVar('match_type', $match_type);
sqGetGlobalVar('value', $value);
+sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
/* end of get globals */
if (isset($theid) && ($action == 'delete') ||
($action == 'up') ||
($action == 'down')) {
+
+ // security check
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
$new_rules = array();
switch($action) {
case('delete'):
exit;
} else if ($action == 'save') {
+ // security check
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
if ($color_type == 1) $newcolor = $newcolor_choose;
elseif ($color_type == 2) $newcolor = $newcolor_input;
else $newcolor = $color_type;
$oTemplate->assign('color_radio', ($selected_choose ? 1 : ($selected_input ? 2 : 0)));
$oTemplate->assign('color_input', ($selected_input ? $color : ''));
- echo addForm('options_highlight.php', 'post', 'f').
+ echo addForm('options_highlight.php', 'post', 'f', '', '', array(), TRUE).
addHidden('action', 'save');
if($action == 'edit') {
echo addHidden('theid', (isset($theid)?$theid:''));
/* SquirrelMail required files. */
require_once(SM_PATH . 'functions/identity.php');
+require_once(SM_PATH . 'functions/forms.php');
/* make sure that page is not available when $edit_identity is false */
if (!$edit_identity) {
sqgetGlobalVar('newidentities', $newidentities, SQ_POST);
sqgetGlobalVar('smaction', $smaction, SQ_POST);
sqgetGlobalVar('return', $return, SQ_POST);
+sqgetGlobalVar('smtoken', $submitted_token, SQ_POST, '');
// First lets see if there are any actions to perform //
if (!empty($smaction) && is_array($smaction)) {
+ // first do a security check
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
$doaction = '';
$identid = 0;
$i[count($i)] = $a;
//FIXME: NO HTML IN THE CORE
-echo '<form name="f" action="options_identities.php" method="post">' . "\n";
+echo '<form name="f" action="options_identities.php" method="post">' . "\n"
+ . addHidden('smtoken', sm_generate_security_token()) . "\n";
$oTemplate->assign('identities', $i);
$oTemplate->display('options_advidentity_list.tpl');
$oTemplate->assign('criteria', $c);
- echo '<form action="../src/search.php" name="form_asearch">' . "\n";
+ echo '<form action="../src/search.php" name="form_asearch">' . "\n"
+ . addHidden('smtoken', sm_generate_security_token()) . "\n";
$oTemplate->display('search_advanced.tpl');
echo "</form>\n";
}
$oTemplate->assign('where_sel', $where);
$oTemplate->assign('what_val', $what);
- echo '<form action="../src/search.php" name="form_asearch">' . "\n";
+ echo '<form action="../src/search.php" name="form_asearch">' . "\n"
+ . addHidden('smtoken', sm_generate_security_token()) . "\n";
$oTemplate->display('search.tpl');
echo "</form>\n";
}
/* ------------------------ main ------------------------ */
/* get globals we will need */
+sqgetGlobalVar('smtoken', $submitted_token, SQ_GET, '');
sqgetGlobalVar('delimiter', $delimiter, SQ_SESSION);
if (!sqgetGlobalVar('checkall',$checkall,SQ_GET)) {
if (!isset($submit)) {
$submit = '';
} else {
+
+ // first validate security token
+ sm_validate_security_token($submitted_token, 3600, TRUE);
+
switch ($submit) {
case $search_button_text:
if (asearch_check_query($where_array, $what_array, $exclude_array) == '') {
$colspan = $abook_has_extra_field ? 6 : 5;
?>
<form action="<?php echo $form_action; ?>" method="post" id="address_book_form" name="address_book_form">
+<input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<div id="addressList">
<table cellspacing="0">
<tr>
<tr>
<td>
<form method="post" action="folders.php" name="cf" id="cf">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<input type="hidden" name="smaction" value="create" />
<input type="text" name="folder_name" size="25" value="" />
<br />
if (!empty($rendel_folder_list)) {
?>
<form method="post" action="folders.php" name="uf" id="uf">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<input type="hidden" name="smaction" value="unsubscribe" />
<select name="folder_names[]" multiple="multiple" size="8">
<?php echo $rendel_folder_list ?>
if ($no_list_for_subscribe) {
?>
<form method="post" action="folders.php" name="sf" id="sf">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<input type="hidden" name="smaction" value="subscribe" />
<input type="text" name="folder_names[]" size="25" />
<input type="submit" value="<?php echo _("Subscribe") ?>" />
} elseif (!empty($subbox_option_list)) {
?>
<form method="post" action="folders.php" name="sf" id="sf">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<input type="hidden" name="smaction" value="subscribe" />
<div>
<?php
?>
<div class="dialogbox">
<form action="folders.php" method="post">
+<input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<table cellspacing="0" class="wrapper">
<?php
if ( $dialog_type == 'rename' ) {
?>
<div id="message_list">
<form id="<?php echo $form_name;?>" name="<?php echo $form_name;?>" method="post" action="<?php echo $php_self;?>">
+<input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<table class="table_empty" cellspacing="0">
<tr>
<td>
if ($can_be_deleted) {
?>
<form name="deleteMessageForm" action="<?php echo $move_delete_form_action; ?>" method="post">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<?php echo $delete_form_extra; ?>
<small>
<input type="submit" name="delete" <?php if ($accesskey_read_msg_delete != 'NONE') echo 'accesskey="' . $accesskey_read_msg_delete . '" '; ?>value="<?php echo _("Delete"); ?>" />
if ($can_be_moved) {
?>
<form name="moveMessageForm" action="<?php echo $move_delete_form_action; ?>" method="post">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<?php echo $move_form_extra; ?>
<small>
<?php echo _("Move To"); ?>:
</tr>
</table>
<form action="../src/addressbook.php" method="post" name="f_add">
+<input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<input type="hidden" name="addaddr[firstname]" value="<?php echo $firstname; ?>" />
<input type="hidden" name="addaddr[lastname]" value="<?php echo $lastname; ?>" />
<table cellspacing="0" class="table1">
</tr>
</table>
</form>
-</div>
\ No newline at end of file
+</div>
if ($can_be_deleted) {
?>
<form name="deleteMessageForm" action="<?php echo $move_delete_form_action; ?>" method="post">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<?php echo $delete_form_extra; ?>
<small>
<input type="submit" name="delete" <?php if ($accesskey_read_msg_delete != 'NONE') echo 'accesskey="' . $accesskey_read_msg_delete . '" '; ?>value="<?php
if ($can_be_moved) {
?>
<form name="moveMessageForm" action="<?php echo $move_delete_form_action; ?>" method="post">
+ <input type="hidden" name="smtoken" value="<?php echo sm_generate_security_token(); ?>" />
<?php echo $move_form_extra; ?>
<small>
<?php echo _("Move To"); ?>: