- shared/platform-tools: ea3616a → 12f26fd (27 commits). Traz o módulo de audit (f761d99, o pin da frota), «manter sessão iniciada», o registo de segurança da frota (o gancho que o login já tinha passa a funcionar) e a correção do IP do cliente encontrada nesta auditoria (último X-Forwarded-For / X-Real-IP), promovida ao partilhado. client_ip() da app delega nele. - config/functions_audit.php: audit() sobre platform_audit(); tabela audit_log criada no primeiro uso, segredos ocultados, nunca parte a ação. - Registado: login, falha de login, bloqueio por excesso de tentativas, logout, pedidos de recuperação, mudança/definição de palavra-passe, registo e criação de utilizadores, convites, perfil (antes/depois), todas as definições (db_update_setting, com valor anterior), cada passo da lixeira (apagar, restaurar, eliminar de vez) e ações nas mensagens. - Sistema → Auditoria (views/sysadmin/audit.php), só sysadmin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
47 lines
1.9 KiB
PHP
47 lines
1.9 KiB
PHP
<?php
|
|
// ─── CSRF helpers (shared) ───────────────────────────────────────────────────
|
|
//
|
|
// Self-contained double-submit-free session token for every POST form rendered
|
|
// by the platform-tools pages (platform_update.php, backups.php) and verified
|
|
// by their handlers. Deliberately independent of any per-host csrf helper —
|
|
// the only contract is the same one the flash messages already rely on: the
|
|
// stub's bootstrap has a PHP session available.
|
|
//
|
|
// Public API:
|
|
// platform_csrf_token(): string — session token (created on first use)
|
|
// platform_csrf_field(): string — ready-to-echo hidden <input>
|
|
// platform_csrf_ok(): bool — validate the token sent with a POST
|
|
|
|
function platform_csrf_token(): string {
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
if (empty($_SESSION['platform_tools_csrf'])) {
|
|
$_SESSION['platform_tools_csrf'] = bin2hex(random_bytes(32));
|
|
}
|
|
return (string) $_SESSION['platform_tools_csrf'];
|
|
}
|
|
|
|
function platform_csrf_field(): string {
|
|
return '<input type="hidden" name="_pt_csrf" value="'
|
|
. htmlspecialchars(platform_csrf_token())
|
|
. '">';
|
|
}
|
|
|
|
function platform_csrf_ok(): bool {
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
$sent = (string) ($_POST['_pt_csrf'] ?? '');
|
|
$have = (string) ($_SESSION['platform_tools_csrf'] ?? '');
|
|
$ok = $sent !== '' && $have !== '' && hash_equals($have, $sent);
|
|
if (!$ok) {
|
|
// Uma sessão expirada também cai aqui; é no hub, com a frota inteira à
|
|
// vista, que um padrão se distingue de um formulário esquecido aberto.
|
|
require_once __DIR__ . '/functions_security_events.php';
|
|
platform_security_event(null, 'csrf_failed',
|
|
(string) (parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH) ?? ''));
|
|
}
|
|
return $ok;
|
|
}
|