mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-05-20 10:21:32 +00:00
Harden backup security: password confirmation, CSRF, env toggle
Address security review feedback from jbtronics: - Add IS_AUTHENTICATED_FULLY to all sensitive endpoints (create/delete backup, delete log, download backup, start update, restore) - Change backup download from GET to POST with CSRF token - Require password confirmation before downloading backups (backups contain sensitive data like password hashes and secrets) - Add DISABLE_BACKUP_DOWNLOAD env var (default: disabled) to control whether backup downloads are allowed - Add password confirmation modal with security warning in template - Add comprehensive tests: auth checks, env var blocking, POST-only enforcement, status/progress endpoint auth
This commit is contained in:
parent
c16b6c7fac
commit
dd8698840d
5 changed files with 257 additions and 43 deletions
|
|
@ -416,12 +416,15 @@
|
|||
</td>
|
||||
<td class="text-end">
|
||||
<div class="btn-group btn-group-sm">
|
||||
{% if is_granted('@system.manage_updates') %}
|
||||
<a href="{{ path('admin_update_manager_backup_download', {filename: backup.file}) }}"
|
||||
class="btn btn-outline-secondary"
|
||||
title="{% trans %}update_manager.backup.download{% endtrans %}">
|
||||
{% if not backup_download_disabled and is_granted('@system.manage_updates') %}
|
||||
<button type="button"
|
||||
class="btn btn-outline-secondary btn-download-backup"
|
||||
data-filename="{{ backup.file }}"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#downloadBackupModal"
|
||||
title="{% trans %}update_manager.backup.download{% endtrans %}">
|
||||
<i class="fas fa-download"></i>
|
||||
</a>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if not backup_restore_disabled and is_granted('@system.manage_updates') %}
|
||||
<form action="{{ path('admin_update_manager_restore') }}" method="post" class="d-inline"
|
||||
|
|
@ -473,4 +476,53 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Password confirmation modal for backup download #}
|
||||
{% if not backup_download_disabled and is_granted('@system.manage_updates') %}
|
||||
<div class="modal fade" id="downloadBackupModal" tabindex="-1" aria-labelledby="downloadBackupModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form action="{{ path('admin_update_manager_backup_download') }}" method="post" id="downloadBackupForm">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="downloadBackupModalLabel">
|
||||
<i class="fas fa-download me-2"></i>{% trans %}update_manager.backup.download{% endtrans %}
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle me-1"></i>
|
||||
{% trans %}update_manager.backup.download.security_warning{% endtrans %}
|
||||
</div>
|
||||
<input type="hidden" name="_token" value="{{ csrf_token('update_manager_download') }}">
|
||||
<input type="hidden" name="filename" id="downloadBackupFilename" value="">
|
||||
<div class="mb-3">
|
||||
<label for="downloadBackupPassword" class="form-label">
|
||||
{% trans %}update_manager.backup.download.password_label{% endtrans %}
|
||||
</label>
|
||||
<input type="password" class="form-control" id="downloadBackupPassword"
|
||||
name="password" required autocomplete="current-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
|
||||
{% trans %}cancel{% endtrans %}
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-download me-1"></i>{% trans %}update_manager.backup.download{% endtrans %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="{{ csp_nonce('script') }}">
|
||||
document.getElementById('downloadBackupModal').addEventListener('show.bs.modal', function (event) {
|
||||
var button = event.relatedTarget;
|
||||
var filename = button.getAttribute('data-filename');
|
||||
document.getElementById('downloadBackupFilename').value = filename;
|
||||
document.getElementById('downloadBackupPassword').value = '';
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue