Skip to content

Commit

Permalink
Update settings_backup.php
Browse files Browse the repository at this point in the history
Testing on MariaDB
  • Loading branch information
aftechro authored Jan 3, 2024
1 parent 051ce46 commit ba22856
Showing 1 changed file with 230 additions and 20 deletions.
250 changes: 230 additions & 20 deletions settings_backup.php
Original file line number Diff line number Diff line change
@@ -1,37 +1,247 @@
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

require_once "inc_all_settings.php";
?>

<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-database mr-2"></i>Download Database</h3>
$backupFolder = 'uploads/backups/';

// Check if the backup folder inside uploads exists, if not, create it
$uploadsBackupsFolder = 'uploads/backups/';
if (!file_exists($uploadsBackupsFolder) || !is_dir($uploadsBackupsFolder)) {
if (!mkdir($uploadsBackupsFolder, 0777, true)) {
die('Failed to create backups folder inside uploads');
}
}

$backups = array_diff(scandir($backupFolder), array('..', '.'));

// Database connection
$mysqli = new mysqli($dbhost, $dbusername, $dbpassword, $database);

if ($mysqli->connect_error) {
die('Database Connection Failed: ' . $mysqli->connect_error);
}

// Handle backup action
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['backup'])) {
// Create a backup
$backupFileName = date("d-m-Y_H-i-s") . ".sql";
$backupPath = $backupFolder . $backupFileName;

// Run mysqldump command to include table content
$escapedBackupPath = escapeshellarg($backupPath);
$command = "mysqldump --complete-insert --skip-comments --host=$dbhost --user=$dbusername --password=$dbpassword $database > $escapedBackupPath";

Check failure

Code scanning / SonarCloud

MySQL database passwords should not be disclosed High

Make sure this MySQL database password gets changed and removed from the code. See more on SonarCloud
exec($command);

// Refresh backup list after creating a new backup
$backups = array_diff(scandir($backupFolder), array('..', '.'));
}

// Handle restore action
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['proceed-restore'])) {
$selectedBackup = $_POST['proceed-restore'];

// Use realpath to get the canonicalized absolute pathname
$sqlFile = realpath($backupFolder . $selectedBackup);

// Check if the obtained path is within the allowed directory
if ($sqlFile !== false && strpos($sqlFile, realpath($backupFolder)) === 0) {
$sqlContent = file_get_contents($sqlFile);

// Remove comments and split into separate queries
$sqlQueries = preg_split('/;(?=(?:[^\'"]*[\'"][^\'"]*[\'"])*[^\'"]*$)/', $sqlContent);

foreach ($sqlQueries as $query) {
$query = trim($query);
if (!empty($query)) {
// Check if the query is an "ALTER TABLE" with "ENABLE KEYS" and skip it
if (strpos($query, 'ALTER TABLE') !== false && strpos($query, 'ENABLE KEYS') !== false) {
continue;
}

// Execute each query separately using $mysqli
$result = $mysqli->query($query);

// Check for execution success
if ($result === false) {
die("Error executing query: " . $mysqli->error);
}
}
}

// Display success message
echo '<div class="alert alert-success" role="alert">Database restore successful!</div>';
} else {
// Log an error or take appropriate action for invalid paths
echo 'Invalid backup path: ' . htmlspecialchars($sqlFile, ENT_QUOTES, 'UTF-8');
}
}


// Handle delete action
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['delete'])) {
$selectedBackup = $_POST['delete'];

// Validate the selectedBackup variable to prevent directory traversal
if (in_array($selectedBackup, $backups)) {
unlink($backupFolder . $selectedBackup);
}
}

// Handle delete selected action
if ($_SERVER["REQUEST_METHOD"] === "POST" && isset($_POST['delete-selected'])) {
// Implement delete selected logic here
if (isset($_POST['selectedBackups'])) {
foreach ($_POST['selectedBackups'] as $selectedBackup) {
$backupPath = $backupFolder . $selectedBackup;

// Validate the file path to prevent directory traversal
$realBackupPath = realpath($backupPath);
$realBackupFolder = realpath($backupFolder);

if ($realBackupPath !== false && $realBackupFolder !== false && strpos($realBackupPath, $realBackupFolder) === 0) {
unlink($backupPath);

Check failure

Code scanning / SonarCloud

I/O function calls should not be vulnerable to path injection attacks High

Change this code to not construct the path from user-controlled data. See more on SonarCloud
} else {
// Log an error or take appropriate action for invalid paths
echo 'Invalid backup path: ' . htmlspecialchars($backupPath, ENT_QUOTES, 'UTF-8');
}
}
}
}

// Reverse the order of backups to display the latest on top
$backups = array_reverse(array_diff(scandir($backupFolder), array('..', '.')));

// Function to format file size in human-readable format
function formatBytes($bytes, $decimals = 2)
{
$size = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];

$factor = floor((strlen($bytes) - 1) / 3);

return sprintf("%.{$decimals}f", $bytes / (1024 ** $factor)) . ' ' . @$size[$factor];
}

?>

<div class="row">
<div class="col-md-6">
<div class="card card-dark mb-3">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-database mr-2"></i>Backup Database</h3>
</div>
<div class="card-body" style="text-align: center;">
<form method="post">
<button type="submit" name="backup" class="btn btn-lg btn-primary"><i class="fas fa-fw fa-save"></i> New Backup</button>
<button type="submit" name="filerestore" value="<?= $backup ?>" class="btn btn-lg btn-warning"><i class="fas fa-fw fa-undo"></i> Restore from file</button>
</form>
</div>
</div>
</div>
<div class="card-body" style="text-align: center;">
<a class="btn btn-primary btn-lg p-3" href="post.php?download_database&csrf_token=<?php echo $_SESSION['csrf_token'] ?>"><i class="fas fa-fw fa-4x fa-download"></i><br><br>Download</a>

<div class="col-md-6">
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-key mr-2"></i>Backup Master Encryption Key</h3>
</div>
<div class="card-body">
<form action="post.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="row d-flex justify-content-center">
<div class="input-group col-8">
<div class="input-group-prepend">
<input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required>
</div>
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-fw fa-key mr-2"></i>Get Master Key</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>

<!-- Display a table with backup list -->
<div class="card card-dark">
<div class="card-header py-3">
<h3 class="card-title"><i class="fas fa-fw fa-key mr-2"></i>Backup Master Encryption Key</h3>
<h3 class="card-title"><i class="fas fa-fw fa-database mr-2"></i>Backup Manager</h3>
</div>
<div class="card-body">
<div class="card-body">
<form action="post.php" method="POST">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token'] ?>">
<div class="row d-flex justify-content-center">
<div class="input-group col-4">
<div class="input-group-prepend">
<input type="password" class="form-control" placeholder="Enter your account password" name="password" autocomplete="new-password" required>
<!-- Backup list table here -->
<form method="post">
<table class="table">
<thead>
<tr>
<th>Select</th>
<th>Backup Name</th>
<th>File Size</th>
<th>Actions</th>
<th>Download</th>
</tr>
</thead>
<tbody>
<?php foreach ($backups as $backup) : ?>
<?php
// Sanitize the file name for use as an HTML ID attribute
$modalId = preg_replace("/[^a-zA-Z0-9]/", "_", $backup);
?>
<tr>
<td><input type="checkbox" name="selectedBackups[]" value="<?= $backup ?>"></td>
<td><?= htmlspecialchars($backup, ENT_QUOTES, 'UTF-8') ?></td>
<td><?= formatBytes(filesize($backupFolder . $backup)) ?></td>
<td>
<button type="button" class="btn btn-warning" data-toggle="modal" data-target="#restoreModal<?= $modalId ?>"><i class="fas fa-fw fa-undo"></i> Restore</button>
<button type="button" class="btn btn-danger" data-toggle="modal" data-target="#deleteModal<?= $modalId ?>"><i class="fas fa-fw fa-trash"></i> Delete</button>
</td>
<td><a href="<?= $backupFolder . $backup ?>" download class="btn btn-info"><i class="fas fa-fw fa-download"></i> Download</a></td>
</tr>

<!-- Restore Modal -->
<div class="modal" id="restoreModal<?= $modalId ?>">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Restore Database</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>Are you sure you want to restore the database from the selected backup?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="submit" name="proceed-restore" value="<?= htmlspecialchars($backup, ENT_QUOTES, 'UTF-8') ?>" class="btn btn-primary">Proceed</button>
</div>
</div>
</div>
</div>
<button class="btn btn-primary" type="submit" name="backup_master_key"><i class="fas fa-fw fa-key mr-2"></i>Get Master Key</button>
</div>
</div>
</form>
</div>

<!-- Delete Modal -->
<div class="modal" id="deleteModal<?= $modalId ?>">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete Backup</h5>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete the backup: <?= htmlspecialchars($backup, ENT_QUOTES, 'UTF-8') ?>?</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="submit" name="delete" value="<?= htmlspecialchars($backup, ENT_QUOTES, 'UTF-8') ?>" class="btn btn-danger">Delete</button>
</div>
</div>
</div>
</div>
<?php endforeach; ?>
</tbody>
</table>
<button type="submit" name="delete-selected" class="btn btn-danger"><i class="fas fa-fw fa-trash"></i> Delete Selected</button>
</form>
</div>
</div>

<?php
require_once "footer.php";

?>

0 comments on commit ba22856

Please sign in to comment.