= $max_attempts && (time() - $rec['last']) < $lockout_sec) { $is_locked = true; $remaining = $lockout_sec - (time() - $rec['last']); $error = "로그인 시도가 너무 많습니다. {$remaining}초 후 다시 시도하세요."; } elseif ($rec['count'] >= $max_attempts && (time() - $rec['last']) >= $lockout_sec) { $attempts = update_attempts($lockout_file, function (array &$a) use ($client_ip) { unset($a[$client_ip]); }); } } // ── CSRF 토큰 생성 ────────────────────────────────────────── if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } // POST 로그인 처리 if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$is_locked && !$configuration_error) { // CSRF 검증 $submitted_token = $_POST['csrf_token'] ?? ''; if (!hash_equals($_SESSION['csrf_token'], $submitted_token)) { $error = '잘못된 요청입니다. 페이지를 새로고침하세요.'; } else { $username = $_POST['username'] ?? ''; $password = $_POST['password'] ?? ''; $otp_code = $_POST['otp_code'] ?? ''; // 비밀번호 검증: bcrypt 해시 $pw_ok = password_verify($password, ADMIN_PASSWORD_HASH); $mfa_ok = $pw_ok && hash_equals(ADMIN_USER, $username) && admin_totp_verify(ADMIN_TOTP_SECRET, $otp_code); if (hash_equals(ADMIN_USER, $username) && $pw_ok && $mfa_ok) { // 성공: 실패 기록 초기화 update_attempts($lockout_file, function (array &$a) use ($client_ip) { unset($a[$client_ip]); }); session_regenerate_id(true); $_SESSION['logged_in'] = true; $_SESSION['login_time'] = time(); // 절대 수명 기준(이후 갱신하지 않음) $_SESSION['last_activity'] = time(); // 유휴 타이머 기준(활동 시 갱신) $_SESSION['username'] = $username; $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); admin_audit_log('login_success', $username, true); // 로그인 시 센서 오프라인 체크 즉시 실행 (쓰로틀 없이) check_offline_sensors(); header('Location: dashboard.php'); exit; } else { // 실패: 카운트 증가(원자적 읽기-수정-쓰기로 lost update 방지) $attempts = update_attempts($lockout_file, function (array &$a) use ($client_ip) { if (!isset($a[$client_ip])) { $a[$client_ip] = ['count' => 0, 'last' => 0]; } $a[$client_ip]['count']++; $a[$client_ip]['last'] = time(); }); $attempt_count = $attempts[$client_ip]['count'] ?? $max_attempts; $left = $max_attempts - $attempt_count; if ($left > 0) { $error = "아이디 또는 비밀번호가 올바르지 않습니다. (남은 시도: {$left}회)"; } else { $error = "로그인 시도가 너무 많습니다. {$lockout_sec}초 후 다시 시도하세요."; } error_log("[LOGIN] 로그인 실패 - IP: {$client_ip}, User: {$username}"); admin_audit_log('login_failure', $username, false, [ 'password_ok' => $pw_ok, 'mfa_present' => preg_replace('/\D+/', '', $otp_code) !== '', ]); } } } ?>
대시보드에 접근하려면 로그인하세요.