request here, so the * existing URLs keep working but an * anonymous visitor gets 401 instead of the file. Only the upload types that * FileUploader accepts are served, and never anything that could execute. */ require_once __DIR__ . '/session.php'; if (empty($_SESSION['login_company_id'])) { http_response_code(401); exit; } // The session is only read from here on; release its lock so pages that load // many images do not queue behind each other. session_write_close(); $types = [ 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'webp' => 'image/webp', 'pdf' => 'application/pdf', ]; $base = realpath(__DIR__ . '/uploads'); $rel = (string)($_GET['path'] ?? ''); $file = $base ? realpath($base . '/' . $rel) : false; // realpath() resolves ../ and symlinks; anything outside uploads/ is refused. if ($base === false || $file === false || !is_file($file) || strpos($file, $base . DIRECTORY_SEPARATOR) !== 0) { http_response_code(404); exit; } $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); if (!isset($types[$ext])) { http_response_code(404); exit; } while (ob_get_level() > 0) { ob_end_clean(); } header('Content-Type: ' . $types[$ext]); header('Content-Length: ' . filesize($file)); header('Content-Disposition: ' . ($ext === 'pdf' ? 'attachment' : 'inline') . '; filename="' . basename($file) . '"'); header('Cache-Control: private, max-age=3600'); header("Content-Security-Policy: default-src 'none'; img-src 'self'; style-src 'unsafe-inline'; sandbox"); header('X-Content-Type-Options: nosniff'); readfile($file);