Carry the CSRF token on fetch requests and put mutations behind POST

Unraid already enforces CSRF on every POST via auto_prepend, but its
injector is jQuery-only — the plugin's native fetch() calls carried no
token and were being terminated before the endpoint ran, silently,
because csrf_terminate exits with an empty body that r.json() swallows.
This commit is contained in:
Gmer4Lfe
2026-08-02 10:28:53 -04:00
parent 987313e7dc
commit c34224effa
16 changed files with 198 additions and 67 deletions
+14 -6
View File
@@ -46,14 +46,14 @@
// Cache-Control: no-store, no-cache. A cached provisioning response would report an old
// key preview after a genuine renewal — the one moment the preview matters.
//
// Known gap: this is a state-changing action served over GET.
// Both callers (pages/setup.php, pages/partnership.php) fetch it, and POST bodies are
// unreliable on this nginx/PHP setup. It is guarded by the Unraid WebGUI session rather
// than by method or token. Documented rather than silently accepted — see the CSRF note
// in README-unraid.md.
// POST only, which is what places it behind Unraid's CSRF guard.
// The platform prepend validates the token on every POST and inspects no GET at all, so
// a state-changing action reachable by GET is a state-changing action with no CSRF
// protection. This was previously a GET; the callers now POST and the token travels as
// an X-CSRF-Token header set by the shim in Varaverk.page. See README-unraid.md.
//
// REQUEST
// GET, no parameters (the target host is the local host, by construction)
// POST, no parameters (the target host is the local host, by construction)
//
// RESPONSE
// {"ok":true,"key_preview":"abcd1234...wxyz"} or "registered" when the key is not readable
@@ -67,6 +67,14 @@ header('Content-Type: application/json');
header('Cache-Control: no-store, no-cache');
require_once dirname(__DIR__) . '/include/config.php';
// POST is what puts this behind Unraid's CSRF guard — the platform prepend validates every
// POST and ignores every GET, so a state-changing action must not be reachable by GET.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['ok' => false, 'error' => 'POST only']);
exit;
}
$host = vv_detect_host();
if (!preg_match('/^host\d+$/', $host)) {
echo json_encode(['ok' => false, 'error' => 'Cannot detect local host']);