diff --git a/server-php/sync.php b/server-php/sync.php index 06423bc..3957775 100644 --- a/server-php/sync.php +++ b/server-php/sync.php @@ -41,10 +41,22 @@ header('X-Content-Type-Options: nosniff'); // Authentication // --------------------------------------------------------------------------- -/** Bearer token, compared in constant time so a wrong guess leaks no timing. */ +/** + * Bearer token, compared in constant time so a wrong guess leaks no timing. + * + * Three sources on purpose: Apache commonly strips Authorization before PHP + * sees it (it is only forwarded with CGIPassAuth on, or through a rewrite), so + * $_SERVER alone answers empty on a stock XAMPP and every request looks + * unauthenticated. getallheaders() reads what the client actually sent. + */ function client_token(): string { $h = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? ''; - if (stripos($h, 'Bearer ') === 0) return substr($h, 7); + if ($h === '' && function_exists('getallheaders')) { + foreach (getallheaders() as $name => $value) { + if (strcasecmp($name, 'Authorization') === 0) { $h = $value; break; } + } + } + if (stripos($h, 'Bearer ') === 0) return trim(substr($h, 7)); return ''; } @@ -85,7 +97,12 @@ if ($method === 'HEAD' || $method === 'GET') { header('Last-Modified: ' . gmdate('D, d M Y H:i:s', (int)filemtime(SYNC_FILE)) . ' GMT'); // Save the round trip when the client already holds this version. - if (trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? '') === $etag) { + // Compared on the bare hash: a validator that loses its quotes or arrives + // as a weak tag (W/"…") would otherwise never match, and every request + // would silently download the whole snapshot again — costly on mobile data + // and invisible in the logs, since a 200 looks perfectly normal. + $sent = trim($_SERVER['HTTP_IF_NONE_MATCH'] ?? ''); + if ($sent !== '' && trim($sent, 'W/" ') === trim($etag, '" ')) { http_response_code(304); exit; }