forked from extern/egroupware
prevent CSRF for setup and admin site configuration
This commit is contained in:
parent
61198e3684
commit
544d57ca46
@ -21,6 +21,12 @@ class uiconfig
|
||||
// allowing inline js
|
||||
egw_framework::csp_script_src_attrs('unsafe-inline');
|
||||
|
||||
// for POST requests validate CSRF token (or terminate request)
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST')
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __CLASS__);
|
||||
}
|
||||
|
||||
if (empty($_GET['appname']) && isset($params['appname']))
|
||||
{
|
||||
$_appname = $params['appname'];
|
||||
@ -172,7 +178,8 @@ class uiconfig
|
||||
$t->set_var('th_text', $GLOBALS['egw_info']['theme']['th_text']);
|
||||
$t->set_var('row_on', $GLOBALS['egw_info']['theme']['row_on']);
|
||||
$t->set_var('row_off', $GLOBALS['egw_info']['theme']['row_off']);
|
||||
$t->set_var('hidden_vars','<input type="hidden" name="referer" value="'.$referer.'">');
|
||||
$t->set_var('hidden_vars', html::input_hidden('referer', $referer).
|
||||
html::input_hidden('csrf_token', egw_csrf::token(__CLASS__)));
|
||||
|
||||
$vars = $t->get_undefined('body');
|
||||
|
||||
|
71
phpgwapi/inc/class.egw_csrf.inc.php
Normal file
71
phpgwapi/inc/class.egw_csrf.inc.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* EGroupware API: CSRF (Cross Site Request Forgery) protection
|
||||
*
|
||||
* @link http://www.egroupware.org
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @package api
|
||||
* @author Ralf Becker <rb@stylite.de>
|
||||
* @copyright (c) 2014 by Ralf Becker <rb@stylite.de>
|
||||
* @version $Id$
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class supplying methods to prevent successful CSRF by requesting a random token,
|
||||
* stored on server and validated when request get posted.
|
||||
*
|
||||
* CSRF token generation used openssl_random_pseudo_bytes, if available, otherwise
|
||||
* mt_rand based auth::randomstring is used.
|
||||
*
|
||||
* CSRF tokens are stored (incl. optional purpose) in user session.
|
||||
*
|
||||
* If a token does not validate (incl. purpose, if specified in generation)
|
||||
* the request will be imediatly terminated.
|
||||
*/
|
||||
class egw_csrf
|
||||
{
|
||||
/**
|
||||
* Get a CSRF token for an optional $purpose, which can be validated
|
||||
*
|
||||
* @param mixed $_purpose=true if given it need to be used in validate too! (It must NOT be NULL)
|
||||
* @return string CSRF token
|
||||
*/
|
||||
public static function token($_purpose=true)
|
||||
{
|
||||
if (is_null($_purpose))
|
||||
{
|
||||
throw new egw_exception_wrong_parameter(__METHOD__.'(NULL) $_purspose must NOT be NULL!');
|
||||
}
|
||||
// generate random token (using oppenssl if available otherwise mt_rand based auth::randomstring)
|
||||
$token = function_exists('openssl_random_pseudo_bytes') ?
|
||||
base64_encode(openssl_random_pseudo_bytes(64)) :
|
||||
auth::randomstring(64);
|
||||
|
||||
// store it in session for later validation
|
||||
egw_cache::setSession(__CLASS__, $token, $_purpose);
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a CSRF token or teminate the request
|
||||
*
|
||||
* @param string $_token CSRF token generated with egw_csfr::token()
|
||||
* @param string $_purpose=true optional purpose string passed to token method
|
||||
* @param boolean $_delete_token=true true if token should be deleted after validation, it will validate no second time
|
||||
*/
|
||||
public static function validate($_token, $_purpose=true, $_delete_token=true)
|
||||
{
|
||||
$stored_purpose = egw_cache::getSession(__CLASS__, $_token);
|
||||
|
||||
// if token and purpose dont validate, log and terminate request
|
||||
if (!isset($stored_purpose) || $stored_purpose !== $_purpose)
|
||||
{
|
||||
error_log('CSRF detected from IP '.$_SERVER['REMOTE_ADDR'].' to '.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI']);
|
||||
if ($_POST) error_log(array2string($_POST));
|
||||
// we are not throwing an exception here, but die, to not allow catching it!
|
||||
die("CSRF detected, request terminated!");
|
||||
}
|
||||
if ($_delete_token) egw_cache::unsetTree (__CLASS__, $_token);
|
||||
}
|
||||
}
|
@ -29,6 +29,13 @@ $setup_tpl->set_file(array(
|
||||
'T_footer' => 'footer.tpl',
|
||||
'T_alert_msg' => 'msg_alert_msg.tpl'
|
||||
));
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
|
||||
// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!)
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST)
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
}
|
||||
|
||||
// determine from where we migrate to what
|
||||
if (!is_object($GLOBALS['egw_setup']->db))
|
||||
|
@ -26,6 +26,12 @@ if (strpos($_SERVER['PHP_SELF'],'admin_account.php') !== false)
|
||||
$error = '';
|
||||
if ($_POST['submit'])
|
||||
{
|
||||
// for POST (not GET or cli call via setup_cmd_admin) validate CSRF token
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST')
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
}
|
||||
|
||||
/* Posted admin data */
|
||||
$passwd = get_var('passwd',Array('POST'));
|
||||
$passwd2 = get_var('passwd2',Array('POST'));
|
||||
@ -83,6 +89,8 @@ if(!$_POST['submit'] || $error)
|
||||
$setup_tpl->set_var('create_demo_accounts',lang('Create demo accounts'));
|
||||
$setup_tpl->set_var('demo_desc',lang('The username/passwords are: demo/guest, demo2/guest and demo3/guest.'));
|
||||
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
|
||||
$setup_tpl->set_var('lang_submit',lang('Save'));
|
||||
$setup_tpl->set_var('lang_cancel',lang('Cancel'));
|
||||
$setup_tpl->pparse('out','T_admin_account');
|
||||
|
@ -40,6 +40,13 @@ $setup_tpl->set_file(array(
|
||||
'T_login_stage_header' => 'login_stage_header.tpl',
|
||||
'T_setup_main' => 'applications.tpl'
|
||||
));
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
|
||||
// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!)
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST)
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
}
|
||||
|
||||
$setup_tpl->set_block('T_login_stage_header','B_multi_domain','V_multi_domain');
|
||||
$setup_tpl->set_block('T_login_stage_header','B_single_domain','V_single_domain');
|
||||
|
@ -32,6 +32,13 @@ $setup_tpl->set_file(array(
|
||||
'T_config_pre_script' => 'config_pre_script.tpl',
|
||||
'T_config_post_script' => 'config_post_script.tpl'
|
||||
));
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
|
||||
// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!)
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST)
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
}
|
||||
|
||||
/* Following to ensure windows file paths are saved correctly */
|
||||
if (function_exists('get_magic_quotes_runtime') && get_magic_quotes_runtime())
|
||||
@ -132,16 +139,14 @@ $setup_tpl->pparse('out','T_config_pre_script');
|
||||
/* Now parse each of the templates we want to show here */
|
||||
class phpgw
|
||||
{
|
||||
var $common;
|
||||
var $accounts;
|
||||
var $applications;
|
||||
var $db;
|
||||
}
|
||||
$GLOBALS['egw'] = new phpgw;
|
||||
$GLOBALS['egw']->common =& CreateObject('phpgwapi.common');
|
||||
$GLOBALS['egw']->db =& $GLOBALS['egw_setup']->db;
|
||||
|
||||
$t = CreateObject('phpgwapi.Template',$GLOBALS['egw']->common->get_tpl_dir('setup'));
|
||||
$t = CreateObject('phpgwapi.Template', common::get_tpl_dir('setup'));
|
||||
|
||||
$t->set_unknowns('keep');
|
||||
$t->set_file(array('config' => 'config.tpl'));
|
||||
|
@ -49,6 +49,13 @@ $setup_tpl->set_file(array(
|
||||
'T_footer' => 'footer.tpl',
|
||||
'T_db_backup' => 'db_backup.tpl',
|
||||
));
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
|
||||
// check CSRF token for POST requests with any content (setup uses empty POST to call it's modules!)
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && $_POST)
|
||||
{
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
}
|
||||
$setup_tpl->set_block('T_db_backup','schedule_row','schedule_rows');
|
||||
$setup_tpl->set_block('T_db_backup','set_row','set_rows');
|
||||
|
||||
|
@ -268,6 +268,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['db'])
|
||||
$setup_tpl->set_var('V_db_filled_block',$db_filled_block);
|
||||
break;
|
||||
case 5:
|
||||
$setup_tpl->set_var('hidden_vars', html::input_hidden('csrf_token', egw_csrf::token(__FILE__)));
|
||||
$setup_tpl->set_var('are_you_sure',lang('ARE YOU SURE?'));
|
||||
$setup_tpl->set_var('really_uninstall_all_applications',lang('REALLY Uninstall all applications'));
|
||||
$setup_tpl->set_var('dropwarn',lang('Your tables will be dropped and you will lose data'));
|
||||
@ -292,6 +293,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['db'])
|
||||
!preg_match('/^[0-9.a-z_]+$/i', $_POST['db_grant_host']) ? 'localhost' : $_POST['db_grant_host']);
|
||||
break;
|
||||
case 'drop':
|
||||
egw_csrf::validate($_POST['csrf_token'], __FILE__);
|
||||
$setup_info = $GLOBALS['egw_setup']->detection->get_versions($setup_info);
|
||||
$setup_info = $GLOBALS['egw_setup']->process->droptables($setup_info);
|
||||
break;
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!-- BEGIN header -->
|
||||
<form action="{action_url}" method="post">
|
||||
{hidden_vars}
|
||||
<table border="0" align="center" width="70%">
|
||||
<tr bgcolor="#486591">
|
||||
<td colspan="2" align="center"><b><font color="#fefefe">{description}</font></b></td>
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!-- BEGIN admin_account -->
|
||||
<form method="post" action="{action_url}">
|
||||
{hidden_vars}
|
||||
<table border="0" width="90%" cellspacing="0" cellpadding="2">
|
||||
<tr>
|
||||
<td>
|
||||
|
@ -15,7 +15,7 @@ function check_all(which)
|
||||
{
|
||||
document.apps.elements[i].checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// -->
|
||||
@ -29,6 +29,7 @@ function check_all(which)
|
||||
</tr>
|
||||
</table>
|
||||
<form name="apps" method="post" action="{action_url}">
|
||||
{hidden_vars}
|
||||
<table width="90%" cellspacing="0" cellpadding="2">
|
||||
<!-- END header -->
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
<!-- begin config_pre_script.tpl -->
|
||||
<form method="post" action="config.php">
|
||||
{hidden_vars}
|
||||
<table border="0" align="center" cellspacing="0" width="90%">
|
||||
<!-- end config_pre_script.tpl -->
|
||||
|
||||
|
@ -70,6 +70,7 @@ function sort_table(id)
|
||||
</script>
|
||||
|
||||
<form method="post" name="backup_form" action="{self}" enctype="multipart/form-data">
|
||||
{hidden_vars}
|
||||
<input name="sortedby" id="sortedby" type="hidden" />
|
||||
<table border="0" align="center" width="98%" cellpadding="5">
|
||||
<!-- BEGIN setup_header -->
|
||||
|
@ -142,6 +142,7 @@
|
||||
</td>
|
||||
<td>
|
||||
<form action="index.php" method="post">
|
||||
{hidden_vars}
|
||||
<input type="hidden" name="oldversion" value="new" />
|
||||
<input type="hidden" name="action" value="REALLY Uninstall all applications" />
|
||||
<input type="submit" name="label" value="{really_uninstall_all_applications}" /> {dropwarn}
|
||||
|
Loading…
Reference in New Issue
Block a user