Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add admin SQL queries page to allow admin to easily query the databas…
…e and store queries for later reference.

Improved UI/UX of members list page in admin area.
Fix various low severity stored and reflected xss vulnerabilities.
Fix CSRF issues in various admin pages.
Code refactoring in various files.
  • Loading branch information
Ahmad Gneady committed Jul 3, 2021
1 parent 0a2aaf5 commit 2dc485e
Show file tree
Hide file tree
Showing 73 changed files with 1,040 additions and 229 deletions.
26 changes: 26 additions & 0 deletions app/admin/ajax-saved-sql.php
@@ -0,0 +1,26 @@
<?php
/*
Manage stored SQL queries for admin user.
Parameters:
queries: (optional) a json string [{name, query}, ..]) to store.
Response:
stored queries (as a json string).
queries are stored in the membership_users.data field for the current user, under the key 'storedQueries'
*/

$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");

if(!csrf_token(true)) {
@header('HTTP/1.0 403 Access Denied');
die();
}

// store queries if provided
if(isset($_REQUEST['queries'])) {
$queries = $_REQUEST['queries'];
setUserData('storedQueries', $queries);
}

echo getUserData('storedQueries');
34 changes: 34 additions & 0 deletions app/admin/ajax-sql.php
@@ -0,0 +1,34 @@
<?php
$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");

if(!csrf_token(true)) {
@header('HTTP/1.0 403 Access Denied');
die();
}

$sql = trim($_REQUEST['sql']);
if(!preg_match('/^SELECT\s+.*?\s+FROM\s+\S+/i', $sql)) {
@header('HTTP/1.0 404 Not Found');
die("Invalid query");
}

// force a limit of 1000 in case no limit specified
if(!preg_match('/\s+limit\s+\d+(\s*,\s*\d+)?/i', $sql))
$sql .= ' LIMIT 1000';

$resp = ['titles' => [], 'data' => [], 'error' => ''];
$eo = ['silentErrors' => true];

$res = sql($sql, $eo);
if(!$res)
$resp['error'] = $eo['error'];
else while($row = db_fetch_assoc($res)) {
if(!count($resp['titles']))
$resp['titles'] = array_keys($row);

$resp['data'][] = array_map('htmlspecialchars', array_values($row));
}

@header('Content-type: application/json');
echo json_encode($resp, JSON_PARTIAL_OUTPUT_ON_ERROR);
3 changes: 2 additions & 1 deletion app/admin/getUsers.php
@@ -1,5 +1,5 @@
<?php
// This script and data application were generated by AppGini 5.97
// This script and data application were generated by AppGini 6.0
// Download AppGini for free from https://bigprof.com/appgini/download/

/*
Expand Down Expand Up @@ -56,6 +56,7 @@
}
$res = sql("select u.memberID, g.name from membership_users u left join membership_groups g on u.groupID=g.groupID left join membership_grouppermissions p on g.groupID=p.groupID and p.tableName='{$table_name}' where {$where} order by g.name, u.memberID limit {$skip}, {$results_per_page}", $eo);
while($row = db_fetch_row($res)) {
$row = array_map('strip_tags', $row);
$prepared_data[] = array('id' => to_utf8($row[0]), 'text' => to_utf8("<b>{$row[1]}</b>/{$row[0]}"));
}

Expand Down
41 changes: 40 additions & 1 deletion app/admin/incFunctions.php
Expand Up @@ -1216,7 +1216,9 @@ function update_membership_users() {
`comments` TEXT,
`pass_reset_key` VARCHAR(100),
`pass_reset_expiry` INT UNSIGNED,
`flags` TEXT,
`allowCSVImport` TINYINT NOT NULL DEFAULT '0',
`data` LONGTEXT,
PRIMARY KEY (`memberID`),
INDEX `groupID` (`groupID`)
) CHARSET " . mysql_charset,
Expand All @@ -1229,6 +1231,7 @@ function update_membership_users() {
sql("ALTER TABLE `{$tn}` ADD INDEX `groupID` (`groupID`)", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `flags` TEXT", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `allowCSVImport` TINYINT NOT NULL DEFAULT '0'", $eo);
sql("ALTER TABLE `{$tn}` ADD COLUMN `data` LONGTEXT", $eo);
}
########################################################################
function update_membership_userrecords() {
Expand Down Expand Up @@ -1582,7 +1585,7 @@ function html_attr($str) {
function html_attr_tags_ok($str) {
// use this instead of html_attr() if you don't want html tags to be escaped
$new_str = html_attr($str);
return str_replace(array('&lt;', '&gt;'), array('<', '>'), $new_str);
return str_replace(['&lt;', '&gt;'], ['<', '>'], $new_str);
}
#########################################################
class Notification{
Expand Down Expand Up @@ -2582,3 +2585,39 @@ function assocArrFilter($arr, $func) {

return $filtered;
}
#########################################################
function setUserData($key, $value = null) {
$data = [];

$user = makeSafe(getMemberInfo()['username']);
if(!$user) return false;

$dataJson = sqlValue("SELECT `data` FROM `membership_users` WHERE `memberID`='$user'");
if($dataJson) {
$data = @json_decode($dataJson, true);
if(!$data) $data = [];
}

$data[$key] = $value;

return update(
'membership_users',
['data' => @json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR)],
['memberID' => $user]
);
}
#########################################################
function getUserData($key) {
$user = makeSafe(getMemberInfo()['username']);
if(!$user) return null;

$dataJson = sqlValue("SELECT `data` FROM `membership_users` WHERE `memberID`='$user'");
if(!$dataJson) return null;

$data = @json_decode($dataJson, true);
if(!$data) return null;

if(!isset($data[$key])) return null;

return $data[$key];
}
1 change: 1 addition & 0 deletions app/admin/incHeader.php
Expand Up @@ -213,6 +213,7 @@ function hideDialogs() {
<li><a href="pageBackupRestore.php"><i class="glyphicon menu-item-icon text-info glyphicon-tasks"></i> <?php echo $Translation['database backups']; ?></a></li>
<li><a href="pageUploadCSV.php"><i class="glyphicon menu-item-icon text-info glyphicon-upload"></i> <?php echo $Translation['import CSV']; ?></a></li>
<li><a href="pageQueryLogs.php"><i class="glyphicon menu-item-icon text-info glyphicon-book"></i> <?php echo $Translation['Query logs']; ?></a></li>
<li><a href="pageSQL.php"><i class="glyphicon menu-item-icon text-danger glyphicon-console"></i> <?php echo $Translation['Interactive SQL queries tool']; ?></a></li>
<li class="divider"></li>
<li><a href="https://forums.appgini.com" target="_blank"><i class="glyphicon menu-item-icon text-info glyphicon-new-window"></i> <?php echo $Translation['AppGini forum']; ?></a></li>
</ul>
Expand Down
2 changes: 2 additions & 0 deletions app/admin/pageDeleteGroup.php
Expand Up @@ -5,6 +5,8 @@
// validate input
$groupID = intval($_GET['groupID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

// make sure group has no members
if(sqlValue("select count(1) from membership_users where groupID='{$groupID}'")) {
errorMsg($Translation["can not delete group remove members"]);
Expand Down
16 changes: 8 additions & 8 deletions app/admin/pageDeleteMember.php
@@ -1,17 +1,17 @@
<?php
$currDir=dirname(__FILE__);
require("$currDir/incCommon.php");
require(__DIR__ . '/incCommon.php');

// validate input
$memberID=makeSafe(strtolower($_GET['memberID']));
$memberID = makeSafe(strtolower($_GET['memberID']));

sql("delete from membership_users where lcase(memberID)='$memberID'", $eo);
sql("update membership_userrecords set memberID='' where lcase(memberID)='$memberID'", $eo);
if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
sql("DELETE FROM `membership_users` WHERE LCASE(`memberID`)='$memberID'", $eo);
sql("UPDATE `membership_userrecords` SET `memberID`='' WHERE LCASE(`memberID`)='$memberID'", $eo);

if($_SERVER['HTTP_REFERER']) {
redirect($_SERVER['HTTP_REFERER'], TRUE);
} else {
redirect("admin/pageViewMembers.php");
redirect('admin/pageViewMembers.php');
}

?>
6 changes: 4 additions & 2 deletions app/admin/pageDeleteRecord.php
@@ -1,10 +1,12 @@
<?php
$currDir = dirname(__FILE__);
require("{$currDir}/incCommon.php");
require(__DIR__ . '/incCommon.php');

// validate input
$recID = intval($_GET['recID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
$res = sql("SELECT `tableName`, `pkValue` FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($row = db_fetch_row($res)) {
sql("DELETE FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
Expand Down
24 changes: 24 additions & 0 deletions app/admin/pageDeleteRecord.php.bak
@@ -0,0 +1,24 @@
<?php

require(__DIR__ . '/incCommon.php');

// validate input
$recID = intval($_GET['recID']);

if(!csrf_token(true)) die($Translation['csrf token expired or invalid']);

$eo = ['silentErrors' => true];
$res = sql("SELECT `tableName`, `pkValue` FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($row = db_fetch_row($res)) {
sql("DELETE FROM `membership_userrecords` WHERE `recID`='{$recID}'", $eo);
if($pkName = getPKFieldName($row[0])) {
sql("DELETE FROM `{$row[0]}` WHERE `{$pkName}`='" . makeSafe($row[1]) . "'", $eo);
}
}

if($_SERVER['HTTP_REFERER']) {
redirect($_SERVER['HTTP_REFERER'], TRUE);
exit;
}

redirect('admin/pageViewRecords.php');
2 changes: 1 addition & 1 deletion app/admin/pageEditMember.php
Expand Up @@ -139,7 +139,7 @@
} elseif($_GET['groupID'] != '') {
// show the form for adding a new member, and pre-select the provided group
$groupID = intval($_GET['groupID']);
$group_name = sqlValue("select name from membership_groups where groupID='$groupID'");
$group_name = strip_tags(sqlValue("select name from membership_groups where groupID='$groupID'"));
if($group_name)
$addend = " to '{$group_name}'";
}
Expand Down
2 changes: 1 addition & 1 deletion app/admin/pageEditMemberPermissions.php
Expand Up @@ -19,7 +19,7 @@
$anonGroupID = sqlValue("select groupID from membership_groups where lcase(name)='" . strtolower(makeSafe($anonymousGroup)) . "'");
$adminGroupID = sqlValue("select groupID from membership_groups where name='Admins'");
$groupID = sqlValue("select groupID from membership_users where lcase(memberID)='{$memberID->sql}'");
$group = sqlValue("select name from membership_groups where groupID='{$groupID}'");
$group = strip_tags(sqlValue("select name from membership_groups where groupID='{$groupID}'"));
if($groupID == $anonGroupID || $memberID->raw == $anonymousMember || !$groupID || $groupID == $adminGroupID || $memberID->raw == $adminConfig['adminUsername']) {
// error in request. redirect to members page.
redirect('admin/pageViewMembers.php');
Expand Down
2 changes: 1 addition & 1 deletion app/admin/pageMail.php
Expand Up @@ -139,7 +139,7 @@
<label class="col-sm-4 col-md-3 col-lg-2 col-lg-offset-2 control-label"><?php echo $Translation["from"]; ?></label>
<div class="col-sm-8 col-md-9 col-lg-6">
<p class="form-control-static">
<?php echo "{$adminConfig['senderName']} &lt;{$adminConfig['senderEmail']}&gt;"; ?>
<?php echo strip_tags($adminConfig['senderName']) . " &lt;{$adminConfig['senderEmail']}&gt;"; ?>
<div>
<a href="pageSettings.php?search-settings=smtp" class="btn btn-default">
<i class="glyphicon glyphicon-pencil"></i>
Expand Down

0 comments on commit 2dc485e

Please sign in to comment.