security: fix auth bypass, XSS, IDOR, command injection, session hardening, rate limiting

- Phase 1: Require authentication for API register endpoint; restrict role creation
- Phase 2: Add $fillable whitelist to Server model to prevent mass assignment
- Phase 3: Fix XSS via escapeHtml in sidebar, JSON_HEX_TAG in script embeds
- Phase 4: Add canView() checks to TerminalController history/clearHistory
- Phase 5: escapeshellarg() on certbot params, sanitize service names, regex-based command blocklist, suppress exception messages
- Phase 6: SESSION_SECURE=true, SameSite=Strict, logout via POST+CSRF, chmod 640 .env
- Phase 7: DB-based rate limiting replacing session-based, applied to API login/register
This commit is contained in:
2026-06-14 07:32:18 -04:00
parent c16236314e
commit a5eb101d98
11 changed files with 145 additions and 38 deletions

View File

@@ -165,7 +165,7 @@ $aggregatedMetrics = $aggregatedMetrics ?? [];
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<script>
const aggregatedData = <?= json_encode($aggregatedMetrics) ?>;
const aggregatedData = <?= json_encode($aggregatedMetrics, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES) ?>;
let aggregatedChart = null;

View File

@@ -132,7 +132,10 @@
</div>
<div class="user-menu">
<a href="/profile" title="Profile"><i class="fas fa-user-cog"></i></a>
<a href="/logout" title="Logout"><i class="fas fa-sign-out-alt"></i></a>
<form method="POST" action="/logout" class="logout-form" style="display:inline">
<input type="hidden" name="_csrf_token" value="<?= $this->csrfToken() ?>">
<button type="submit" title="Logout" style="background:none;border:none;color:inherit;cursor:pointer;padding:0;font:inherit;line-height:1"><i class="fas fa-sign-out-alt"></i></button>
</form>
</div>
</div>
</div>
@@ -407,7 +410,7 @@ function toggleServerList(e) {
const status = s.current_status === 'online' ? 'success' : s.current_status === 'offline' ? 'danger' : 'muted';
html += '<a href="/servers/' + s.id + '" class="nav-sub-item">'
+ '<span class="status-dot-sm status-' + status + '"></span> '
+ s.name + '</a>';
+ escapeHtml(s.name) + '</a>';
});
sub.innerHTML = html;
} else {

View File

@@ -47,7 +47,7 @@ $totalPages = max(1, ceil(count($services) / $perPage));
</div>
<script>
const allServices = <?= json_encode($services) ?>;
const allServices = <?= json_encode($services, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES) ?>;
const perPage = <?= $perPage ?>;
function applyFilters() {

View File

@@ -371,7 +371,7 @@ $canManage = $server_role === 'owner' || $server_role === 'manager';
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
<script>
const historicalData = <?= json_encode($historicalMetrics) ?>;
const historicalData = <?= json_encode($historicalMetrics, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES) ?>;
let metricsChart = null;