forked from extern/egroupware
use etemplate-exec-id as CSRF token for ajax requests
This commit is contained in:
parent
2045c08e54
commit
d95894d530
@ -339,9 +339,12 @@ class admin_account
|
||||
*
|
||||
* @param int $account_id
|
||||
* @param String[] $data Optional data
|
||||
* @param string $etemplate_exec_id to check against CSRF
|
||||
*/
|
||||
public static function ajax_delete_group($account_id, $data)
|
||||
public static function ajax_delete_group($account_id, $data, $etemplate_exec_id)
|
||||
{
|
||||
Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args());
|
||||
|
||||
$cmd = new admin_cmd_delete_account(Api\Accounts::id2name(Api\Accounts::id2name($account_id)), null, false, (array)$data['admin_cmd']);
|
||||
$msg = $cmd->run();
|
||||
|
||||
|
@ -335,12 +335,15 @@ class admin_acl
|
||||
* Checks access and throws an exception, if a change is attempted without proper access
|
||||
*
|
||||
* @param string|array $ids "$app:$account:$location" string used as row-id in list
|
||||
* @param int $rights =null null to delete, or new rights
|
||||
* @param Array $values =array() Additional values from UI
|
||||
* @param int $rights null to delete, or new rights
|
||||
* @param array $values Additional values from UI
|
||||
* @param string $etemplate_exec_id to check against CSRF
|
||||
* @throws Api\Exception\NoPermission
|
||||
*/
|
||||
public static function ajax_change_acl($ids, $rights=null, $values = array())
|
||||
public static function ajax_change_acl($ids, $rights, $values, $etemplate_exec_id)
|
||||
{
|
||||
Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args());
|
||||
|
||||
try {
|
||||
foreach((array)$ids as $id)
|
||||
{
|
||||
|
@ -288,13 +288,17 @@ class admin_customfields
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a type over ajax. Used when Policy is involved, otherwise
|
||||
* things go normally
|
||||
* Delete a type over ajax.
|
||||
*
|
||||
* @param Array $content
|
||||
* Used when Policy is involved, otherwise things go normally
|
||||
*
|
||||
* @param array $content
|
||||
* @param string $etemplate_exec_id to check against CSRF
|
||||
*/
|
||||
public function ajax_delete_type($content)
|
||||
public function ajax_delete_type($content, $etemplate_exec_id)
|
||||
{
|
||||
Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args());
|
||||
|
||||
// Read fields
|
||||
$this->appname = $content['appname'];
|
||||
$this->fields = Api\Storage\Customfields::get($content['appname'],true);
|
||||
|
@ -1568,10 +1568,13 @@ class admin_mail
|
||||
* domain => mailLocalAddress,
|
||||
* status => mail activation status('active'|'')
|
||||
* )
|
||||
* @param string $etemplate_exec_id to check against CSRF
|
||||
* @return json response
|
||||
*/
|
||||
public function ajax_activeAccounts($_data)
|
||||
public function ajax_activeAccounts($_data, $etemplate_exec_id)
|
||||
{
|
||||
Api\Etemplate\Request::csrfCheck($etemplate_exec_id, __METHOD__, func_get_args());
|
||||
|
||||
if (!$this->is_admin) die('no rights to be here!');
|
||||
$response = Api\Json\Response::get();
|
||||
if (($account = $GLOBALS['egw']->accounts->read($_data['id'])))
|
||||
|
@ -4,9 +4,8 @@
|
||||
* @link http://www.egroupware.org
|
||||
* @package filemanager
|
||||
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2013-16 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @copyright (c) 2013-20 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
|
||||
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||
* @version $Id: app.js 56051 2016-05-06 07:58:37Z ralfbecker $
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -36,7 +35,7 @@ app.classes.admin = AppJS.extend(
|
||||
nm: null,
|
||||
|
||||
/**
|
||||
* Refarence to div to hold AJAX loadable pages
|
||||
* Reference to div to hold AJAX loadable pages
|
||||
*
|
||||
* {et2_box}
|
||||
*/
|
||||
@ -443,7 +442,7 @@ app.classes.admin = AppJS.extend(
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
this.egw.json('admin_account::ajax_delete_group', [account_id, _action.data]).sendRequest();
|
||||
this.egw.json('admin_account::ajax_delete_group', [account_id, _action.data, this.et2._inst.etemplate_exec_id]).sendRequest();
|
||||
break;
|
||||
default:
|
||||
if (!_action.data.url)
|
||||
@ -510,7 +509,7 @@ app.classes.admin = AppJS.extend(
|
||||
var callback = function(_button_id, _value) {
|
||||
if(_button_id != et2_dialog.OK_BUTTON) return;
|
||||
|
||||
var request = egw.json(className+'::ajax_change_acl', [ids,null,_value], this._acl_callback,this,false,this)
|
||||
var request = egw.json(className+'::ajax_change_acl', [ids, null, _value, this.et2._inst.etemplate_exec_id], this._acl_callback,this,false,this)
|
||||
.sendRequest();
|
||||
}.bind(this);
|
||||
|
||||
@ -674,7 +673,7 @@ app.classes.admin = AppJS.extend(
|
||||
{
|
||||
// Changed the account or location, remove previous or we
|
||||
// get a new line instead of an edit
|
||||
this.egw.json(className+'::ajax_change_acl', [content.id, 0], null,this,false,this)
|
||||
this.egw.json(className+'::ajax_change_acl', [content.id, 0, [], this.et2._inst.etemplate_exec_id], null,this,false,this)
|
||||
.sendRequest();
|
||||
}
|
||||
id = [id];
|
||||
@ -708,11 +707,11 @@ app.classes.admin = AppJS.extend(
|
||||
// Remove any removed
|
||||
if(removed.length > 0)
|
||||
{
|
||||
this.egw.json(className+'::ajax_change_acl', [removed, 0], callback ? callback : this._acl_callback,this,false,this)
|
||||
this.egw.json(className+'::ajax_change_acl', [removed, 0, [], this.et2._inst.etemplate_exec_id], callback ? callback : this._acl_callback,this,false,this)
|
||||
.sendRequest();
|
||||
}
|
||||
}
|
||||
this.egw.json(className+'::ajax_change_acl', [id, rights, _value], callback ? callback : this._acl_callback,this,false,this)
|
||||
this.egw.json(className+'::ajax_change_acl', [id, rights, _value, this.et2._inst.etemplate_exec_id], callback ? callback : this._acl_callback,this,false,this)
|
||||
.sendRequest();
|
||||
}
|
||||
},this),
|
||||
@ -971,7 +970,7 @@ app.classes.admin = AppJS.extend(
|
||||
value,
|
||||
{appname: this.getRoot().getArrayMgr('content').getEntry('content_types[appname]')}
|
||||
);
|
||||
egw.json('admin.admin_customfields.ajax_delete_type', [values]).sendRequest();
|
||||
egw.json('admin.admin_customfields.ajax_delete_type', [values, this.getInstanceManager().etemplate_exec_id]).sendRequest();
|
||||
|
||||
// Immediately remove the type
|
||||
var types = this.getRoot().getWidgetById('types');
|
||||
@ -1025,7 +1024,6 @@ app.classes.admin = AppJS.extend(
|
||||
*
|
||||
* @param {egw_action} _action
|
||||
* @param {array} _selected selected users
|
||||
* @todo remove under construction message
|
||||
*/
|
||||
emailadminActiveAccounts: function (_action, _selected){
|
||||
|
||||
@ -1035,7 +1033,7 @@ app.classes.admin = AppJS.extend(
|
||||
|
||||
for (var i=0;i< Object.keys(_selected).length;i++)
|
||||
{
|
||||
accounts[i] = {id:_selected[i]['id'].split('::')[1],qouta:"", domain:"", status:_action.id == 'active'?_action.id:''};
|
||||
accounts[i] = [{id:_selected[i]['id'].split('::')[1],qouta:"", domain:"", status:_action.id == 'active'?_action.id:''}, this.et2._inst.etemplate_exec_id];
|
||||
}
|
||||
var callbackDialog = function (btn){
|
||||
if (btn === et2_dialog.YES_BUTTON)
|
||||
|
@ -291,9 +291,12 @@ class Auth
|
||||
}
|
||||
|
||||
/**
|
||||
* return a random string of letters [0-9a-zA-Z] of size $size
|
||||
* return a random string of size $size either just alphanumeric or with special chars
|
||||
*
|
||||
* @param $size int-size of random string to return
|
||||
* @param $use_specialchars =false false: only letters and numbers, true: incl. special chars
|
||||
* @return string
|
||||
* @throws \Exception if it was not possible to gather sufficient entropy.
|
||||
*/
|
||||
static function randomstring($size, $use_specialchars=false)
|
||||
{
|
||||
@ -310,13 +313,10 @@ class Auth
|
||||
$random_char = array_merge($random_char, str_split(str_replace('\\', '', self::SPECIALCHARS)), $random_char);
|
||||
}
|
||||
|
||||
// use cryptographically secure random_int available in PHP 7+
|
||||
$func = function_exists('random_int') ? 'random_int' : 'mt_rand';
|
||||
|
||||
$s = '';
|
||||
for ($i=0; $i < $size; $i++)
|
||||
{
|
||||
$s .= $random_char[$func(0, count($random_char)-1)];
|
||||
$s .= $random_char[random_int(0, count($random_char)-1)];
|
||||
}
|
||||
return $s;
|
||||
}
|
||||
|
@ -142,9 +142,10 @@ class Request
|
||||
* the sesison to constantly grow).
|
||||
*
|
||||
* @param string $id =null
|
||||
* @return Request
|
||||
* @param bool $handle_not_found =true true: handle not found by trying to redirect, false: just return null
|
||||
* @return Request|null null if Request not found and $handle_not_found === false
|
||||
*/
|
||||
public static function read($id=null)
|
||||
public static function read($id=null, $handle_not_found=true)
|
||||
{
|
||||
if (is_null(self::$request_class))
|
||||
{
|
||||
@ -192,7 +193,7 @@ class Request
|
||||
//error_log(__METHOD__."() size of request = ".bytes($id));
|
||||
}
|
||||
}
|
||||
if (!$request) // eT2 request/session expired
|
||||
if (!$request && $handle_not_found) // eT2 request/session expired
|
||||
{
|
||||
list($app) = explode('.', $_GET['menuaction']);
|
||||
$global = false;
|
||||
@ -228,6 +229,33 @@ class Request
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* CSRF check using an etemplate-exec-id
|
||||
*
|
||||
* If eTemplate request object could not be read, the function will NOT return,
|
||||
* but send an Ajax error response and exit or die with the error-message!
|
||||
*
|
||||
* @param string $id etemplate-exec-id
|
||||
* @param string $caller calling method to log
|
||||
* @param array $args =[] arguments to log
|
||||
* @throws Api\Json\Exception
|
||||
*/
|
||||
public static function csrfCheck($id, $caller, $args=[])
|
||||
{
|
||||
if (!self::read($id, false)) // false: do NOT handle not found, but return null
|
||||
{
|
||||
error_log(__METHOD__."('$id', $caller, ".json_encode($args).") called with invalid/expired etemplate_exec_id: possible CSRF detected from IP ".$_SERVER['REMOTE_ADDR'].' to '.$_SERVER['REQUEST_METHOD'].' '.$_SERVER['REQUEST_URI']);
|
||||
$msg = lang('Request could not be processed, please reload your window (press F5 or Cmd R)!');
|
||||
|
||||
if (Api\Json\Request::isJSONRequest())
|
||||
{
|
||||
Api\Json\Response::get()->message($msg, 'error');
|
||||
exit;
|
||||
}
|
||||
die($msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor to force the instancation of this class only via it's static factory method read
|
||||
*
|
||||
@ -381,17 +409,15 @@ class Request
|
||||
* creates a new unique request-id
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception if it was not possible to gather sufficient entropy.
|
||||
*/
|
||||
static function request_id()
|
||||
{
|
||||
// replace url-unsafe chars with _ to not run into url-encoding issues when used in a url
|
||||
$userID = preg_replace('/[^a-z0-9_\\.@-]/i', '_', $GLOBALS['egw_info']['user']['account_lid']);
|
||||
|
||||
// generate random token (using oppenssl if available otherwise mt_rand based Auth::randomstring)
|
||||
$token = function_exists('openssl_random_pseudo_bytes') ?
|
||||
// replace + with _ to not run into url-encoding issues when used in a url
|
||||
str_replace('+', '_', base64_encode(openssl_random_pseudo_bytes(32))) :
|
||||
\EGroupware\Api\Auth::randomstring(44);
|
||||
// replace + with _ to not run into url-encoding issues when used in a url
|
||||
$token = str_replace('+', '_', base64_encode(random_bytes(32)));
|
||||
|
||||
return $GLOBALS['egw_info']['flags']['currentapp'].'_'.$userID.'_'.$token;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user