diff --git a/README.md b/README.md index 7fb1236..daca8f1 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,8 @@ Worker 교체나 예외 종료로 `processing`, `ui_timeout_pending` 상태가 ## 보안 - 차량 API는 API token 또는 허용 IP 정책을 사용합니다. +- 차량 모니터 화면과 `monitor.php?mode=ajax`, `monitor.php?mode=usage`는 런처 공통 인증 쿠키가 유효할 때만 응답합니다. +- 런처 공통 인증은 `https://chaegeon.com/custom/launcher/api.php?session=1` 응답으로 확인하며, `/mnt` 아래 NFS 마운트 경로를 런타임 fallback으로 사용하지 않습니다. - 차량 제어 명령은 허용된 명령 코드로 제한합니다. - Secret 파일은 저장소 밖에서 제한된 권한으로 유지합니다. - 실제 제어 명령은 최신 상태 조회와 명령 검증 이후에만 전송합니다. diff --git a/monitor.php b/monitor.php index 1ae87e7..2b4cf17 100644 --- a/monitor.php +++ b/monitor.php @@ -540,7 +540,107 @@ function json_response(array $payload, int $status = 200): void exit; } +function car_monitor_launcher_session(): array +{ + static $session = null; + + if (is_array($session)) { + return $session; + } + + $cookieHeader = trim((string)($_SERVER['HTTP_COOKIE'] ?? '')); + if ($cookieHeader === '') { + $session = ['authenticated' => false, 'admin' => false]; + return $session; + } + + $url = 'https://chaegeon.com/custom/launcher/api.php?session=1'; + $body = false; + + if (function_exists('curl_init')) { + $ch = curl_init($url); + if ($ch !== false) { + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CONNECTTIMEOUT => 2, + CURLOPT_TIMEOUT => 4, + CURLOPT_HTTPHEADER => ['Cookie: ' . $cookieHeader], + CURLOPT_USERAGENT => 'car-monitor-auth/1.0', + ]); + $body = curl_exec($ch); + curl_close($ch); + } + } + + if ($body === false) { + $context = stream_context_create([ + 'http' => [ + 'method' => 'GET', + 'timeout' => 4, + 'header' => "Cookie: {$cookieHeader}\r\nUser-Agent: car-monitor-auth/1.0\r\n", + ], + ]); + $body = @file_get_contents($url, false, $context); + } + + $data = is_string($body) ? json_decode($body, true) : null; + $session = is_array($data) ? $data : ['authenticated' => false, 'admin' => false]; + + return $session; +} + +function car_monitor_has_access(): bool +{ + $session = car_monitor_launcher_session(); + return !empty($session['authenticated']); +} + +function car_monitor_require_access(bool $json = false): void +{ + if (car_monitor_has_access()) { + return; + } + + if ($json) { + json_response([ + 'status' => 'unauthorized', + 'message' => '런처 인증이 필요합니다.', + ], 401); + } + + http_response_code(401); + header('Content-Type: text/html; charset=utf-8'); + header('Cache-Control: no-cache, no-store, must-revalidate'); + ?> + + + + + +차량 모니터 인증 + + + +
+

인증이 필요합니다

+

런처에서 관리자 세션 또는 공통 비밀번호 인증을 완료한 뒤 차량 모니터에 접속할 수 있습니다.

+런처로 이동 +
+ + + 'success', @@ -637,6 +739,8 @@ if (isset($_GET['mode']) && $_GET['mode'] === 'usage') { ], 500); } } + +car_monitor_require_access(false); ?>