Wellcoms Remote Control v1.0 — 원격 제어 기능 기획서

2026.06.18

Wellcoms Remote Control v1.0 — 원격 제어 기능 기획서

기존 /remote-support/ WebRTC 화면공유 인프라를 확장하여 원격 키보드/마우스 제어 기능을 추가.
고객은 전용 Windows .exe 다운로드, 관리자는 기존 브라우저 뷰어에서 그대로 제어.

  • URL: https://www.wellcoms.co.kr/remote-support/
  • 플러그인: wp-content/plugins/wellcoms-remote/ (v2.0.0 → v3.0.0)
  • 신규 컴포넌트: wellcoms-remote-client/ (Windows .exe, 별도 GitLab 리포)
  • 기준일: 2026-06-18
  • 예상 기간: 5-6주 (Week 0 스파이크 + Week 1-5 개발, v1.0 출시 기준)

1. 개요

1.1 배경

/remote-support/ 페이지는 Phase 1(화면 공유/시청)이 완성된 상태로 운영 중이며, 개발문서 12장에 명시된 Phase 3 "원격 조작 (포터블 .exe 필요)" 단계에 해당한다.

1.2 목표

고객이 다운로드한 Windows .exe를 실행하면 기존과 동일하게 4자리 PIN이 발급되고, 관리자가 PIN으로 연결하여 화면 시청 + 키보드/마우스 제어를 수행할 수 있도록 한다. 단, 기존 브라우저 기반 "화면만 보기" 경로는 100% 보존한다(fallback 용도).

1.3 핵심 원칙

원칙 내용
기존 인프라 최대 재사용 시그널링 서버/TURN/플러그인 구조는 그대로, 단 시그널링 서버는 관리자 토큰 검증 로직 추가 필요 (기존 Phase 1의 보안 구멍을 Phase 3에서 수습)
관리자 UX 동일 기존 브라우저 PIN 입력 → 화면 시청 흐름 유지, "제어"는 토글
고객 경로 2종 유지 (A) 브라우저만 — 화면 보기 / (B) .exe 다운로드 — 화면 + 제어
보안 최우선 관리자 토큰 인증, 사용자 승인, 세션 표시, 만료, 감사 로그
점진적 확장 v1 = 최소 제어 / v2 = 파일전송·클립보드 / v3 = 음성·UAC

⚠️ 기존 "서버 코드 0줄 변경" 주장은 철회됨. Momus 검토 결과 시그널링 서버가 관리자를 인증하지 않는 보안 구멍 발견. 자세한 내용은 §4.4 참조.

1.4 v1.0 성공 기준 (Definition of Done)

다음 항목 모두 충족 시 v1.0 출시:

# 기준 검증 방법
1 Windows 11 + Windows 10 22H2에서 .exe 정상 실행 테스트 매트릭스 통과
2 PIN 발급 후 관리자 브라우저에서 30초 내 화면 표시 E2E 테스트
3 관리자 "원격 제어" 토글 → 고객 승인 → 마우스 클릭/키보드 입력 E2E 동작 수동 시나리오
4 입력 지연 < 150ms (P2P) / < 250ms (TURN 릴레이) chrome://webrtc-internals 측정
5 관리자 토큰 없는 join_room 시도 → 거부 (PIN 유출 시에도 안전) 보안 테스트
6 고객이 "제어 중지" 클릭 → 1초 내 입력 채널 차단 수동 시나리오
7 PIN 5회 오입력 → 5분 임금 (기존 동작 유지) 자동화 테스트
8 30분 세션 자동 종료 자동화 테스트
9 기존 브라우저 "화면만 보기" 경로 100% 동작 (회귀 없음) 회귀 테스트
10 VirusTotal 70개 이상 AV 통과 (false positive 없음) 제출 전 확인
11 주요 브라우저(Chrome/Edge/Firefox 최신) 입력 캡처 정상 브라우저 매트릭스
12 세션 로그 DB 기록 (pin, ip, 시간, 제어 여부) DB 확인

1.5 대상 사용자

역할 행동 필요 조건
고객 (제어당함) /remote-support/ → .exe 다운로드 → 실행 → PIN 안내 → 승인 Windows 10 1903+
관리자 (제어함) /remote-support/ (로그인) → PIN 입력 → "원격 제어" 토글 ON → 마우스/키보드 조작 WP 관리자 권한, 데스크탑 브라우저

1.6 범위 (In/Out)

범위 항목
In Windows .exe 클라이언트 / WebRTC DataChannel 입력 채널 / 관리자 뷰어 입력 오버레이 / 사용자 승인 프롬프트 / 세션 빨간 테두리 표시 / 기본 감사 로그
Out (v1) macOS/Linux 클라이언트 / 음성 통화 / 파일 전송 / 클립보드 동기화 / UAC 권한 앱 제어 / 세션 녹화 / 다중 모니터 전환 UI / 자동 업데이트 (수동 배포만)

2. 시스템 아키텍처

2.1 전체 구조

graph TB
    subgraph "고객 Windows"
        EXE["wellcoms-remote-client.exe
(C# .NET 8 + WPF)"] CAP["Windows.Graphics.Capture
화면 캡처 (하드웨어 가속)"] SI["user32!SendInput
키보드/마우스 주입"] EXE --> CAP EXE --> SI end subgraph "wellcoms.co.kr 서버 (대부분 기존 그대로)" WP["wellcoms_wp
WordPress + wellcoms-remote 플러그인 v3.0"] SIG["wellcoms_signal
Node.js WebSocket :3000
(관리자 토큰 검증 추가 — §4.4)"] TURN["wellcoms_turn
coturn :3478 (변경 없음)"] end subgraph "관리자 브라우저 (기존 뷰어 확장)" ADM["/remote-support/
(관리자 로그인)"] VIEW["📺 비디오 뷰어 (기존)"] INPUT["🖱️⌨️ 입력 오버레이 (신규)"] ADM --> VIEW ADM --> INPUT end CAP -->|"WebRTC Video Track"| VIEW INPUT -->|"WebRTC DataChannel 'input'"| SI EXE -->|"WSS 시그널링"| SIG ADM -->|"WSS 시그널링"| SIG CAP -.->|"P2P/TURN 릴레이"| VIEW INPUT -.->|"P2P/TURN 릴레이"| SI

2.2 WebRTC 연결 구조

graph LR
    subgraph "PeerConnection (1개)"
        VT["Video Track
(고객 → 관리자)"] DC_VID["DataChannel 'ctrl'
(양방향 제어 메시지)"] DC_IN["DataChannel 'input'
(관리자 → 고객 입력 이벤트)"] end EXE_C["고객 .exe"] --> VT EXE_C --> DC_VID ADM_C["관리자 브라우저"] --> DC_VID ADM_C --> DC_IN DC_IN --> EXE_C

핵심: 기존 비디오 트랙 1개에 DataChannel 2개를 추가한다. PeerConnection 자체는 1개이므로 NAT/TURN 협상 로직이 그대로 재사용된다.

2.3 Docker 구성 (signal 서버에 환경변수만 추가)

기존 docker-compose.yml에 WR_SECRET 환경변수만 추가:

services:
  wellcoms_db: ...           # MariaDB (동일)
  wordpress: ...             # WordPress (플러그인만 v3.0으로 업그레이드)
  wellcoms_signal:
    image: wellcoms-signal:latest
    environment:
      SIGNAL_PORT: "3000"
      WR_SECRET: ${WR_SECRET}   # 신규 — WordPress와 공유 (HMAC 서명용, §4.4)
  wellcoms_turn: ...         # TURN (동일)

신규 컨테이너 없음, 신규 포트 없음. signal 서버 이미지만 코드 수정 후 rebuild.

2.4 네트워크 프로토콜 (기존 + 확장)

기존 시그널링 메시지

타입 방향 설명 v1 변경
create_room 클라→서버 방 생성 동일
join_room {pin} 클라→서버 PIN 참여 {pin, token} 으로 확장 — 토큰 없으면 view만 (§4.4)
sdp_offer/answer 릴레이 WebRTC 협상 동일
ice_candidate 릴레이 ICE 후보 교환 동일

신규 DataChannel 메시지 (P2P 직접, 서버 경유 안 함)

채널 타입 방향 페이로드
input mouse_move 관리자→고객 {x, y} (절대 좌표, 원격 해상도 기준)
input mouse_down 관리자→고객 {button: 'left'\|'right'\|'middle'}
input mouse_up 관리자→고객 {button}
input mouse_wheel 관리자→고객 {deltaX, deltaY}
input key_down 관리자→고객 {vk: 'VK_RETURN', code: 13}
input key_up 관리자→고객 {vk, code}
ctrl request_control 관리자→고객 {} 제어 권한 요청
ctrl control_granted 고객→관리자 {} 승인됨
ctrl control_denied 고객→고객 {} 거절됨
ctrl control_revoked 고객→관리자 {} 고객이 제어 중단
ctrl hello 양방향 {version, capabilities} 핸드셰이크

3. 구성요소별 설계

3.1 Windows 클라이언트 (.exe)

3.1.1 기술 스택

계층 기술 이유
언어/런타임 C# .NET 8 Windows API 접근 최고, 단일 exe 배포 용이
UI 프레임워크 WPF PIN 표시 + 상태 표시에 충분, 간단한 UI
WebRTC SIPSorcery (sipsorcery-media) C# WebRTC 라이브러리, Apache 2.0, 비디오 트랙 + DataChannel 지원
화면 캡처 Windows.Graphics.Capture API Win10 1903+ 공식, 하드웨어 가속, 커서 포함, 다중 모니터 지원
입력 주입 user32!SendInput P/Invoke 시스템 레벨 키/마우스, UAC 권한 앱 외 전부
인코딩 H.264 (MediaFoundation) 하드웨어 인코딩, 1080p 30fps 5Mbps 가능
배포 Single-file publish + ZIP .exe 단일 파일 (~30-50MB) + ZIP 묶음

3.1.2 화면 캡처 파이프라인

graph LR
    DWM["DWM Compositor"] --> GC["GraphicsCaptureItem"]
    GC --> DF["Direct3D11CaptureFramePool"]
    DF --> TXT["D3D11Texture2D
(GPU 메모리)"] TXT --> ENC["MediaFoundation H.264 Encoder
(하드웨어 가속)"] ENC --> RTS["RTP 패킷"] RTS --> PC["RTCRtpSender"] PC -.->|"WebRTC Video Track"| ADM_C["관리자 브라우저"]

포인트: Direct3D11 텍스처를 H.264로 직접 인코딩 → CPU/GPU 오버헤드 최소화.

3.1.3 입력 주입 로직

// 마우스 무브 (절대 좌표 0..65535 정규화)
private static void MouseMove(int x, int y) {
    var input = new INPUT {
        type = INPUT_MOUSE,
        u = new InputUnion {
            mi = new MOUSEINPUT {
                dx = x * 65535 / Screen.PrimaryScreen.Bounds.Width,
                dy = y * 65535 / Screen.PrimaryScreen.Bounds.Height,
                dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE
            }
        }
    };
    SendInput(1, ref input, Marshal.SizeOf(input));
}


// 키보드 (가상 키 코드)
private static void KeyPress(ushort vk, bool down) {
    var input = new INPUT {
        type = INPUT_KEYBOARD,
        u = new InputUnion {
            ki = new KEYBDINPUT {
                wVk = vk,
                dwFlags = down ? 0 : KEYEVENTF_KEYUP
            }
        }
    };
    SendInput(1, ref input, Marshal.SizeOf(input));
}

3.1.4 클라이언트 상태 머신

stateDiagram-v2
    [*] --> Launching: exe 실행
    Launching --> Connecting: WSS 연결 시도
    Connecting --> Waiting: create_room → PIN 발급
    Waiting --> Connected: 관리자 입장 (peer_joined)
    Connected --> Signaling: SDP/ICE 교환
    Signaling --> Streaming: P2P 연결 완료
    Streaming --> AwaitingApproval: 관리자 request_control
    AwaitingApproval --> Controlled: 사용자 승인
    AwaitingApproval --> Streaming: 사용자 거절/타임아웃
    Controlled --> Streaming: 관리자/사용자 제어 중지
    Controlled --> Controlled: 입력 이벤트 수신
    Streaming --> Ended: 연결 종료
    Waiting --> Ended: 30분 타임아웃
    Ended --> [*]

3.1.5 UI (WPF 단일 창)

┌──────────────────────────────────────┐
│ 웰컴스 원격 지원              [_][□][×]│
├──────────────────────────────────────┤
│                                       │
│          4 8 2 7                      │
│      (PIN, 48pt 굵게)                 │
│                                       │
│   📺 화면 공유 중 (관리자 대기)        │
│                                       │
│   [복사] [소리로 듣기]                 │
│                                       │
│ ─────────────────────────────────────│
│ ℹ️ 이 번호를 전화로 안내해주세요       │
│ ⚠️ 원격 제어 요청 시 이 창에서         │
│    반드시 [허용]을 눌러주세요          │
│                                       │
│ [공유 중지]                            │
└──────────────────────────────────────┘

3.2 관리자 뷰어 확장 (wellcoms-remote.php)

3.2.1 변경 사항 요약

기존 shortcode [wellcoms_screen_share] 내부에 다음을 추가:

  1. DataChannel 'input' 생성 (관리자가 Offer 시 createDataChannel)
  2. <video> 위 입력 오버레이 (투명 div로 마우스 캡처)
  3. "원격 제어" 토글 버튼 (기본 OFF — 보기 전용)
  4. 제어 요청 모달 (고객에게 승인 프롬프트 표시)
  5. 관리자 커서 시각화 (고객 화면에 커서 위치 표시)

3.2.2 입력 오버레이 (JS 의사코드)

// 비디오 위에 투명 div를 올려서 모든 입력 이벤트 가로채기
const overlay = document.createElement('div');
overlay.className = 'wr-input-overlay';
overlay.style.display = 'none';  // 기본 OFF


// 토글 ON 시
function enableControl() {
  if (!inputDC || inputDC.readyState !== 'open') {
    toast('제어 채널이 없습니다', 'error'); return;
  }
  // 고객에게 제어 권한 요청
  inputDC.send(JSON.stringify({type: 'request_control'}));
  // 고객 승인 대기 모달 표시
  showControlRequestModal();
}


overlay.addEventListener('mousemove', e => {
  const rect = video.getBoundingClientRect();
  const scaleX = video.videoWidth / rect.width;
  const scaleY = video.videoHeight / rect.height;
  const x = Math.floor((e.clientX - rect.left) * scaleX);
  const y = Math.floor((e.clientY - rect.top) * scaleY);
  inputDC.send(JSON.stringify({type: 'mouse_move', x, y}));
});


overlay.addEventListener('mousedown', e => {
  const btn = e.button === 0 ? 'left' : e.button === 2 ? 'right' : 'middle';
  inputDC.send(JSON.stringify({type: 'mouse_down', button: btn}));
});


// wheel, keydown, keyup, mouseup, contextmenu(차단) 유사 패턴

3.2.3 좌표 변환

graph LR
    MM["관리자 마우스
clientX, clientY"] --> RECT["video.getBoundingClientRect()"] RECT --> SCALE["videoWidth / rect.width"] SCALE --> NORM["원격 해상도 기준 x, y"] NORM --> DC["DataChannel mouse_move"] DC --> EXE["고객 .exe 수신"] EXE --> SI["SendInput 절대 좌표 (0..65535)"]

주의점:

  • 비디오가 object-fit: contain이면 레터박스 영역은 무시해야 함 (rect와 실제 비디오 영역이 다름)
  • 줌/전체화면 모드에서도 비율 유지
  • 고객 모니터 해상도가 관리자 화면보다 크면 정밀도 저하 → v2에서 가속도/상대좌표 모드 옵션

3.3 시그널링 서버 (관리자 토큰 검증 추가)

⚠️ 기존 "변경 없음" 주장은 철회. §4.4에서 설명한 보안 구멍(누구나 PIN으로 join 가능)을 막기 위해 관리자 토큰 검증 로직이 추가되어야 한다.

3.3.1 변경 사항

signal/server.jsjoin_room 케이스에 토큰 검증을 추가:

// AS-IS (취약)
case 'join_room': {
    const pin = String(msg.pin || '');
    // ... PIN만 체크
}


// TO-BE (안전)
case 'join_room': {
    const pin = String(msg.pin || '');
    const adminPayload = verifyAdminToken(msg.token);
    if (!adminPayload) {
        send(ws, { type: 'error', message: '관리자 인증 필요' });
        ws.close(1008, 'unauthorized');
        return;
    }
    ws._admin_id = adminPayload.admin_id;
    ws._admin_name = adminPayload.admin_name;
    // ... 이후 기존 로직
    send(room.host, { type: 'peer_joined', admin_name: adminPayload.admin_name });
}

3.3.2 v1 호환성 전략

  • Phase 1 브라우저 고객(viewer)용 호환 모드: 토큰 없는 join도 3개월간 허용하되 제어 권한 없음 (view only). 이후 강제.
  • Phase 3 .exe 고객용: 토큰 없는 join 시도 → 즉시 거부

3.3.3 docker-compose.yml 변경

wellcoms_signal 컨테이너에 환경변수 추가:

wellcoms_signal:
  image: wellcoms-signal:latest
  environment:
    SIGNAL_PORT: "3000"
    WR_SECRET: ${WR_SECRET}  # WordPress와 공유 (HMAC 서명용)

.env 파일에 WR_SECRET=<랜덤 64바이트 hex> 추가.

3.3.4 세션 로깅 (부가)

토큰에서 얻은 admin_id를 DB 세션 로그에 기록:

// peer_joined 시
db.query('INSERT INTO wp_wr_sessions SET ?', {
    pin, customer_ip: host._ip, admin_user_id: adminPayload.admin_id,
    started_at: new Date()
});

3.4 워드프레스 플러그인 확장

3.4.1 신규 메뉴/기능

위치 기능
/remote-support/ 고객 페이지 "원격 제어용 다운로드" 버튼 추가 (기존 "즉시 화면 공유"는 유지)
/remote-support/ 관리자 페이지 "원격 제어" 토글 버튼, 입력 캡처 활성화
WP Admin → 원격지원 다운로드 URL 설정, 버전 표시, 세션 로그 뷰어
REST API /wp-json/wr/v1/client/version 최신 버전 + 다운로드 URL 반환 (자동 업데이트용, v1에서는 하드코딩 가능)

3.4.2 세션 감사 로그 (DB)

CREATE TABLE wp_wr_sessions (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  pin VARCHAR(4) NOT NULL,
  customer_ip VARCHAR(45),
  admin_user_id BIGINT,
  started_at DATETIME,
  ended_at DATETIME,
  control_enabled TINYINT DEFAULT 0,
  control_duration_sec INT DEFAULT 0,
  exit_reason VARCHAR(32),  -- 'admin_disconnect' | 'customer_disconnect' | 'timeout' | 'error'
  INDEX idx_pin (pin),
  INDEX idx_started (started_at)
);

로깅 시점: create_room 시 INSERT, peer_left/cleanup 시 UPDATE.

3.5 .exe 배포 전략

3.5.1 문제 상황

유료 코드 서명 인증서($200-400/년) 없이 Windows .exe를 배포할 때:

  • Edge/Chrome 다운로드 차단: "일반적으로 다운로드하지 않음" 경고
  • SmartScreen: "Windows가 PC를 보호했습니다" 파란 창
  • Windows Defender: 휴리스틱 false positive 가능성
  • MOTW (Mark of the Web): 인터넷에서 다운로드된 파일 자동 의심

3.5.2 옵션 분석

옵션 비용 UX 평점 SmartScreen 우회 AV false positive 비고
Microsoft Trusted Signing $9.99/월 ⭐⭐⭐⭐⭐ ✅ (MS 자체 인증서) 낮음 Azure 클라우드 서명, 파트너 센터 계정 필요 (1-2주 셋업)
Certum Open Source ~$30/년 ⭐⭐⭐ ⚠️ 평판 축적 1-3개월 보통 OV 인증서, 사업자 등록 불필요 (개인 OK)
Sectigo/SSL.com OV $80-200/년 ⭐⭐⭐⭐ ⚠️ 평판 축적 1-3개월 낮음 사업자 확인 필요
.zip 묶음 + 안내 무료 ⭐⭐ ❌ (사용자 클릭 유도) 보통 현재 가장 현실적 무료 옵션
MSIX + Microsoft Store 무료 + $19 일회 ⭐⭐⭐⭐ ✅ (MS 서명) 낮음 샌드박스 제한으로 SendInput 불가 → 부적합
PowerShell 부트스트랩 무료 ⭐⭐ ❌ (MOTW 여전함) 보통 .cmd 파일이 PS 다운로드/실행
자체 서명 + 인증서 설치 무료 높음 사용자에게 인증서 설치 요구 → 부적합

3.5.3 추천 전략 (2단계)

1단계 (v1 출시, 무료): .zip 묶음 + 다운로드 페이지 상세 안내

  • 고객은 wellcoms-remote-v1.zip 다운로드 (내부에 .exe 1개 + 사용법.txt)
  • ZIP은 Edge/Chrome에서 직접 .exe보다 덜 공격적으로 차단
  • /remote-support/ 페이지에 다운로드 방법 스크린샷 + 음성 안내
  • 관리자가 전화로 "파란 창 뜨면 '추가 정보' → '실행' 누르세요" 안내 (이미 전화 연결 상태이므로 마찰 적음)
  • PE 헤더에 버전/회사/저작권 정보 명시 (일부 AV 휴리스틽 완화)

2단계 (v1.1+, $10/월): Microsoft Trusted Signing 도입

  • SmartScreen 경고 완전 제거
  • Azure Trusted Signing 서비스 가입 ($9.99/월, 비즈니스 인증 필요 1-2주)
  • 빌드 파이프라인에서 자동 서명
  • 월 50건 트래픽에 $10/월이면 충분히 가성비

3.5.4 .exe 자체 최적화 (서명과 무관)

  • PE 헤더 메타데이터: Version Info, Company Name, Copyright, Description 명시
  • 매니페스트: requestedExecutionLevel="asInvoker" (UAC 프롬프트 회피)
  • DPI 인식: Windows 10/11 호환성 매니페스트 포함
  • AV 제출: VirusTotal 제출 + Microsoft Security Intelligence false positive 제보
  • 단일 파일 publish: .NET trim + PublishSingleFile=true (의존성 누락 방지)
  • Rust 고려: C# .NET 단일 파일 exe는 일부 AV(특히 Kaspersky)에서 false positive 높음. Rust는 훨씬 낮음. 만약 AV 문제 심하면 Rust + libdatachannel로 전환 검토

3.5.5 다운로드 페이지 UX

┌──────────────────────────────────────────────────┐
│ 🖥️ 원격 지원 — Windows 프로그램 다운로드            │
├──────────────────────────────────────────────────┤
│                                                   │
│  [💼 wellcoms-remote-v1.zip 다운로드] (28MB)      │
│                                                   │
│  📋 사용 순서:                                     │
│  1. 위 버튼 클릭 → 다운로드                         │
│  2. 다운로드된 ZIP 파일 압축 해제                   │
│  3. wellcoms-remote.exe 실행                       │
│  4. 화면에 표시된 4자리 번호를 전화로 안내           │
│                                                   │
│  ⚠️ "Windows가 PC를 보호했습니다" 파란 창이 뜨면:    │
│     → [추가 정보] 클릭                             │
│     → [실행] 버튼 클릭                             │
│                                                   │
│  📞 문의: 02-XXXX-XXXX                            │
│                                                   │
└──────────────────────────────────────────────────┘

3.6 통신 흐름 (전체 시퀀스)

sequenceDiagram
    actor C as 고객
    participant EXE as .exe 클라이언트
    participant SIG as 시그널링 서버
    participant ADM as 관리자 브라우저
    actor A as 관리자


    Note over C,A: Phase 1 — PIN 발급

    C->>EXE: wellcoms-remote.exe 실행
    EXE->>SIG: WSS 연결
    EXE->>SIG: create_room
    SIG-->>EXE: room_created {pin: "4827"}
    EXE-->>C: PIN 4827 표시
    C->>A: (전화) "4827번입니다"


    Note over C,A: Phase 2 — 관리자 연결

    A->>ADM: /remote-support/ 접속 (로그인)
    A->>ADM: PIN 4827 입력 + 연결
    ADM->>SIG: join_room {pin}
    SIG-->>EXE: peer_joined
    SIG-->>ADM: peer_joined


    Note over C,A: Phase 3 — WebRTC 협상

    EXE->>SIG: sdp_offer (video track + DataChannels)
    SIG->>ADM: sdp_offer
    ADM->>SIG: sdp_answer
    SIG->>EXE: sdp_answer
    Note over EXE,ADM: ICE candidates 교환


    Note over C,A: Phase 4 — 스트리밍 + 제어

    EXE-->>ADM: Video Track (고객 화면)
    ADM-->>A: 

4. 보안 설계

4.1 위협 모델

graph LR
    subgraph "위협"
        T1["무단 제어"]
        T2["PIN 브루트포스"]
        T3["세션 하이재킹"]
        T4["입력 인젝션 악용"]
        T5["변조된 exe 배포"]
    end

    subgraph "대응"
        D1["WP 관리자 인증 + 승인 프롬프트"]
        D2["PIN 5회 실패 시 5분 차단 (기존)"]
        D3["WSS TLS + SRTP (기존)"]
        D4["제어는 명시적 허용 후에만, 언제든 철회 가능"]
        D5["HTTPS 다운로드 + SHA256 체크섬 게시"]
    end

    T1 --> D1
    T2 --> D2
    T3 --> D3
    T4 --> D4
    T5 --> D5

4.2 보안 체크리스트

항목 구현 v1
Signaling 서버 WSS 기존 (Caddy TLS)
관리자 인증 MangBoard 레벨 10+ 또는 manage_options (기존)
PIN 만료 30분 (기존)
PIN 시도 제한 IP당 5회/5분 (기존)
WebRTC 암호화 SRTP (기존)
사용자 승인 모달 관리자 request_control 시 고객 화면에 모달 ✅ 신규
제어 철회 버튼 고객이 언제든 "제어 중지" 클릭 ✅ 신규
세션 빨간 테두리 제어 중일 때 고객 화면 4면 빨간 테두리 ✅ 신규
감사 로그 DB에 세션 기록 (pin, ip, 시간, 제어 여부) ✅ 신규
exe 체크섬 공개 다운로드 페이지에 SHA256 표시 ✅ 신규
DataChannel 인증 hello 메시지로 버전/역할 검증 ⚠️ v1.1
세션 자동 종료 60분 강제 종료 (옵션) ⚠️ v1.1

4.3 사용자 승인 프롬프트 (.exe 측)

┌──────────────────────────────────────┐
│ ⚠️ 원격 제어 요청                       │
├──────────────────────────────────────┤
│                                       │
│  상담원이 회원님의 컴퓨터를             │
│  직접 조작하려고 합니다.                │
│                                       │
│  • 마우스와 키보드를 제어할 수 있습니다 │
│  • 언제든 [제어 중지]로 중단 가능합니다 │
│  • 세션 종료 시 모든 권한이 사라집니다  │
│                                       │
│  [거절]      [10초 후 자동 거절]       │
│             [✓ 허용]                   │
│                                       │
└──────────────────────────────────────┘

기본 동작: 15초 내 응답 없으면 자동 거절 (보수적 설계).

승인 모달에 표시되는 관리자 정보 (토큰 인증 도입 시 — §4.4):

✓ wellcoms.co.kr 인증된 관리자
  이름: 김철수 (jskim)
  접속 시각: 2026-06-18 14:32

4.4 관리자 토큰 인증 (필수 — 신규 추가)

⚠️ 이 섹션은 Momus 검토에서 발견된 critical 보안 구멍에 대한 대응이다.
기존 signal/server.jsjoin_room {pin} 메시지를 받으면 PIN만 맞으면 무조건 viewer로 등록한다.
WordPress의 wr_is_admin() 체크는 페이지 렌더링 시점에만 수행되며, WS 연결 자체를 인증하지 않는다.
Phase 1(보기 전용)에서는 감수 가능했으나, Phase 3(제어 권한 부여)에서는 심각한 취약점이 된다.

4.4.1 공격 시나리오 (방어하지 않을 경우)

1. 공격자가 정당한 고객의 PIN을 우연히/사회공학적으로 입수
   (예: 전화 도청, 화면 캡처, PIN 재사용 등)
2. 공격자가 브라우저 개발자 도구 또는 커스텀 WS 클라이언트로
   wss://wellcoms.co.kr/signal/ 에 직접 연결
3. {type: 'join_room', pin: '4827'} 전송 → 서버가 무조건 수락
4. 고객 화면 WebRTC 스트림 수신 → **개인정보 유출**
5. 여기에 Phase 3 제어 권한이 더해지면:
   - {type: 'request_control'} 전송
   - 고객이 "관리자가 요청했다"고 착각하고 승인
   - 공격자가 마우스/키보드 완전 제어 → **랜섬웨어 설치, 금융 정보 탈취 등**

4.4.2 해결책: 관리자 토큰 (HMAC 서명)

sequenceDiagram
    participant A as 관리자 브라우저
    participant WP as WordPress (PHP)
    participant SIG as Signal 서버
    participant EXE as 고객 .exe

    Note over A,WP: 페이지 로드 시
    A->>WP: /remote-support/ 접속
    WP->>WP: wr_is_admin() 검증
    WP->>WP: 토큰 생성
{admin_id, exp, nonce} + HMAC-SHA256(WR_SECRET) WP-->>A: 페이지에 토큰 주입 (JS 변수) Note over A,SIG: WS 연결 시 A->>SIG: join_room {pin, token} SIG->>SIG: 토큰 검증 (HMAC + 만료 + nonce) alt 토큰 유효 SIG-->>A: peer_joined SIG->>EXE: peer_joined {admin_name, admin_id} EXE->>EXE: "김철수 관리자 연결됨" 표시 else 토큰 무효 SIG-->>A: error "인증 실패" end

4.4.3 토큰 명세

생성 (PHP, 플러그인 내):

function wr_generate_admin_token() {
    if (!wr_is_admin()) return null;
    $payload = [
        'admin_id' => get_current_user_id(),
        'admin_name' => wp_get_current_user()->display_name,
        'exp' => time() + 300,  // 5분 유효
        'nonce' => wp_create_nonce('wr_admin_token'),
    ];
    $payload_b64 = base64_encode(json_encode($payload));
    $sig = hash_hmac('sha256', $payload_b64, WR_SECRET);
    return $payload_b64 . '.' . $sig;
}

검증 (Node.js, signal/server.js에 추가):

const WR_SECRET = process.env.WR_SECRET;  // docker-compose로 주입


function verifyAdminToken(token) {
    if (!token || !WR_SECRET) return null;
    const [payload_b64, sig] = token.split('.');
    if (!payload_b64 || !sig) return null;

    const expectedSig = crypto
        .createHmac('sha256', WR_SECRET)
        .update(payload_b64)
        .digest('hex');

    if (sig !== expectedSig) return null;  // 서명 불일치

    let payload;
    try { payload = JSON.parse(Buffer.from(payload_b64, 'base64')); }
    catch { return null; }

    if (Date.now() / 1000 > payload.exp) return null;  // 만료
    return payload;  // {admin_id, admin_name, exp, nonce}
}


// join_room 처리 시 (기존 코드 수정)
case 'join_room': {
    const payload = verifyAdminToken(msg.token);
    if (!payload) {
        send(ws, { type: 'error', message: '관리자 인증 실패' });
        ws.close(1008, 'unauthorized');
        return;
    }
    ws._admin_id = payload.admin_id;
    ws._admin_name = payload.admin_name;
    // ... 기존 room 매칭 로직 ...
    // 고객에게 admin_name 전달
    send(room.host, { type: 'peer_joined', admin_name: payload.admin_name });
}

WR_SECRET 공유: docker-compose.yml에 환경변수로 동일한 값 주입 (WP와 signal 컨테이너 양쪽).

4.4.4 v1과 v2 호환성

  • 기존 브라우저 고객 (토큰 없는 브라우저 viewer): Phase 1 호환을 위해 토큰 없는 join도 일단 허용하되 제어 권한은 부여하지 않음 (view only)
  • 새 .exe 고객 + 토큰 있는 관리자: 완전한 기능 (view + control)
  • 마이그레이션: 3개월 후 토큰 강제 (Phase 1 브라우저 viewer도 토큰 발급받도록 플러그인 업데이트)

4.5 입력 채널 추가 보안

4.5.1 입력 이벤트 속도 제한 (Rate Limiting)

악성/버그 관리자가 입력 이벤트 폭탄으로 고객 .exe에 부하를 가하는 것을 방지:

이벤트 최대 주기 초과 시
mouse_move 120/sec (8ms 간격) 드롭
mouse_down/up 30/sec 드롭
mouse_wheel 20/sec 드롭
key_down/up 30/sec 드롭

고객 .exe에서 버퍼와 타임스탬프로 검사. 초과 분은 조용히 무시.

4.5.2 브라우저 예약 키 필터링 (관리자 측)

관리자 브라우저에서 다음 키 조합은 고객에게 전송하지 않고 브라우저가 처리하도록 허용:

이유
F5, Ctrl+R 새로고침 (관리자가 의도한 것)
Ctrl+W 탭 닫기
Ctrl+T, Ctrl+N 새 탭/창
Ctrl+Shift+T 닫은 탭 복원
Alt+Tab OS 창 전환 (브라우저가 안 잡지만 전송 의미 없음)
F11 전체화면 토글
Ctrl+L 주소창 포커스
Ctrl+ +/-/0 브라우저 줌

구현: keydown 핸들러에서 이 키들은 e.preventDefault() 하지 않고 return.

4.5.3 관리자 입력 캡처 중 절대 차단 키

다음 키는 고객에게 보내면 안 되는 위험한 키이므로 하드코트 차단:

이유
Ctrl+Alt+Del OS 레벨 (애초에 SendInput으로 안 됨)
Win 단독 시작 메뉴 (고객 혼란)
Alt+F4 고객 앱 강제 종료 위험

5. UI/UX 설계

5.1 고객 .exe UI 흐름

graph TD
    L["exe 실행"] --> CONN["WSS 연결 중..."]
    CONN --> WAIT["PIN 표시 대기 화면"]
    WAIT --> STREAM["📺 화면 공유 중 + 관리자 대기"]
    STREAM --> CTRL_REQ["⚠️ 관리자 제어 요청 모달"]
    CTRL_REQ -->|"허용"| ACTIVE["🔴 제어 중 (빨간 테두리)"]
    CTRL_REQ -->|"거절"| STREAM
    ACTIVE -->|"관리자/고객 중지"| STREAM
    STREAM --> END["종료"]

5.2 관리자 브라우저 UI (확장)

기존 관리자 화면에 다음 요소 추가:

<!-- 기존: 비디오 + 전체화면 + 연결종료 버튼 -->
<div class="wr-toolbar">
  <button id="btnFullscreen">전체화면</button>
  <!-- 신규 -->
  <button id="btnControlToggle">🎮 원격 제어</button>
  <button id="btnDisconnect">연결 종료</button>
</div>


<!-- 비디오 위 투명 오버레이 (제어 시 표시) -->
<div id="inputOverlay" class="wr-input-overlay">
  <!-- 마우스 이벤트 캡처용 -->
  <div id="remoteCursor"></div>
</div>


<!-- 상태 표시 추가 -->
<div class="wr-status">
  <span class="wr-dot active"></span>
  <span>시청 중</span>
  <!-- 신규 -->
  <span id="ctrlStatus" class="wr-ctrl-status">보기 전용</span>
</div>

5.3 시각적 피드백 (고객 측)

상태 표시
PIN 대기 중 파란 점 + "관리자 대기 중"
시청 중 (보기만) 초록 점 + "화면 공유 중" + 기본 창
제어 요청 수신 노란 점 + 깜빡임 + 승인 모달
제어 활성화 빨간 테두리 + "원격 제어 중" + 닫기 방지 확인

6. 구현 단계 (5-6주)

Week 0: 기술 검증 스파이크 (3-5일, 병렬 수행 가능)

🚨 필수 스파이크. SIPSorcery가 비디오 + DataChannel 동시 지원하는지 먼저 검증한다.
실패 시 전체 계획 재검토 (Rust + libdatachannel 전환 등).

작업 상세 검증 기준
SIPSorcery 동시 채널 스파이크 비디오 송출 + DataChannel 'test' 양방향 메시지 콘솔 앱에서 10초간 안정 동작
H.264 하드웨어 인코딩 스파이크 MediaFoundation으로 1080p30 인코딩 → RTP CPU <15%, 30fps 유지
SendInput 정확도 스파이크 메모장에 가상 키보드 입력, 마우스 클릭 100% 정확도
GraphicsCapture 다중 모니터 스파이크 주 모니터 자동 캡처 부팅 직후 캡처 시작
Go/No-Go 결정 위 스파이크 결과로 진행 여부 판단 4개 모두 PASS → 진행

실패 시 폴백:

  • SIPSorcery 불안정 → Rust + libdatachannel + gfx-rs 전환 (개발 기간 +2주)
  • GraphicsCapture 복잡 → SharpDX 또는 ScreenCaptureLib 래퍼 사용

Week 1: 기반 구축 + .exe 프로토타입

작업 상세 산출물
리포 초기화 wp-wellcoms-remote-client GitLab 리포 생성 빈 리포
C# 프로젝트 셋업 .NET 8 WPF + SIPSorcery NuGet 빌드 가능한 skeleton
WS 클라이언트 기존 시그널링 프로토콜 (create_room, sdp_offer 등) 그대로 구현 기존 signal 서버와 PIN 발급까지 연동
최소 UI WPF 창 + PIN 4자리 표시 + 상태 라벨 PIN 발급 표시
통합 테스트 기존 관리자 브라우저로 PIN 연결 가능한지 음성/화면 공유 없이 연결 성공
관리자 토큰 검증 추가 signal/server.jsverifyAdminToken() 추가 (§4.4) 토큰 없는 join 차단
WP 토큰 발급 wr_generate_admin_token() PHP 함수, 페이지에 주입 관리자 페이지에서 토큰 사용 가능

Week 2: 화면 캡처 + 비디오 스트리밍

작업 상세 산출물
GraphicsCapture 통합 Windows.Graphics.Capture API로 화면 캡처 D3D11Texture 획득
H.264 인코딩 MediaFoundation 하드웨어 인코더 RTP 패킷 송출
WebRTC 비디오 트랙 SIPSorcery RTP 송신 + SDP 협상 기존 브라우저에서 .exe 화면 보임
DataChannel 'ctrl' 추가 createDataChannel('ctrl') P2P 메시지 채널 확보
통합 테스트 브라우저 관리자 → .exe 고객 화면 실시간 시청 v1 마일스톤 (시청만)

Week 3: 입력 제어 채널

작업 상세 산출물
DataChannel 'input' 추가 관리자→고객 입력 이벤트 채널 입력 메시지 수신
SendInput P/Invoke user32.dll 마우스/키보드 주입 입력 이벤트 실행
입력 메시지 디코딩 JSON → INPUT 구조체 매핑 mouse_move, key_down 등 처리
관리자 브라우저 입력 오버레이 <video> 위 투명 div로 이벤트 캡처 관리자 마우스 → 고객 화면
좌표 변환 브라우저 비디오 크기 ↔ 원격 해상도 정확한 클릭 위치
통합 테스트 클릭, 드래그, 타이핑 E2E 동작 v1 핵심 기능 완성

Week 4: 보안 + UX 폴리싱

작업 상세 산출물
사용자 승인 모달 (.exe) request_control 시 WPF 모달 명시적 허가
빨간 테두리 표시 (.exe) 제어 중 창 테두리 + always-on-top 시각적 경고
제어 중지 버튼 (.exe) 고객이 1클릭으로 제어 철회 안전장치
관리자 토글 UX "원격 제어" 버튼 활성/비활성 상태 직관적 토글
관리자 커서 표시 (옵션) 고객 화면에 관리자 커서 위치 표시 피드백
세션 감사 로그 DB 스키마 + 이벤트 로깅 감사 추적
예외 처리 연결 끊김, 입력 채널 오류, 캡처 실패 안정성

Week 5: 배포 + 테스트

작업 상세 산출물
빌드 파이프라인 Single-file publish + ZIP 묶음 배포 아티팩트
PE 헤더 최적화 버전 정보, 회사명, 매니페스트 AV false positive 완화
VirusTotal 제출 주요 AV 통과 확인 배포 전 검증
워드프레스 다운로드 페이지 /remote-support/에 다운로드 섹션 + 안내 사용자 진입점
REST 버전 API /wp-json/wr/v1/client/version 향후 자동 업데이트용
E2E 테스트 다양한 Windows 환경 (10/11, DPI, 멀티모니터) 호환성 확보
내부 베타 실제 관리자가 사용해보며 피드백 런칭 준비

7. 리스크와 대응

리스크 확률 영향 대응
SIPSorcery WebRTC 안정성 높음 백업: Google WebRTC 네이티브 C++ 래퍼. 또는 Rust + libdatachannel 전환
UAC 권한 앱 제어 불가 높음 v1은 일반 앱만. v2에서 서비스 모드 추가 (관리자 권한으로 설치)
SmartScreen 경고로 사용자 포기 높음 높음 1단계: ZIP+안내. 2단계: Microsoft Trusted Signing ($10/월)
AV false positive (C# .NET) PE 헤더 최적화, VirusTotal 모니터링, MS Security Intelligence 제보. 심하면 Rust 전환
화면 캡처 성능 부족 낮음 H.264 하드웨어 인코딩 필수. 1080p 30fps 5Mbps 목표. 못하면 해상도/프레임 조정
좌표 정확도 (줌/전체화면) 낮음 object-fit contain 영역 계산 로직 검증. v2에서 캘리브레이션 모드
관리자 브라우저 입력 포커스 오버레이가 포커스 가로채도 기본 동작 차단 (preventDefault). F11 전체화면 권장
관리자 모바일 제어 불가 높음 낮음 v1은 데스크탑만 명시. 모바일은 "시청만" 지원 (기존 기능 유지)
SIPSorcery 개발 중단 낮음 높음 백업 계획 문서화. WebRTC 네이티브 전환 로드맵
다중 모니터 캡처 낮음 v1은 주 모니터만. 모니터 선택 UI는 v1.1

8. 테스트 계획

8.1 단위 테스트

컴포넌트 테스트 항목
.exe SendInput 가상 마우스/키보드 이벤트 → 올바른 INPUT 구조체 생성
.exe 좌표 변환 0..65535 정규화 로직 경계값
.exe WS 클라이언트 재연결, 타임아웃, 잘못된 메시지
관리자 좌표 스케일 비디오 rect ↔ 원격 해상도 매핑

8.2 통합 테스트

시나리오 검증 항목
정상 연결 .exe 실행 → PIN → 관리자 연결 → 화면 표시
제어 활성화 토글 ON → 승인 → 마우스 이동 → 화면에서 커서 이동
제어 철회 고객 "제어 중지" → 즉시 입력 채널 닫힘
네트워크 끊김 ICE disconnect → 1.5초 후 재연결 시도
PIN 만료 30분 후 자동 종료
동시 세션 관리자 2명 동일 PIN → 거부

8.3 호환성 매트릭스

환경 v1 목표
Windows 11 (최신) ✅ 완벽 지원
Windows 10 22H2 ✅ 완벽 지원
Windows 10 1903+ ✅ 지원 (GraphicsCapture 최소 요구)
Windows 8.1 / 7 ❌ 미지원
관리자 Chrome 120+
관리자 Edge 120+
관리자 Firefox 115+
관리자 Safari 17+ ⚠️ 제한 (WebRTC DataChannel 안정성)
관리자 모바일 ❌ (시청만, 기존 기능)

8.4 성능 목표

지표 목표 측정
지연 (입력 → 화면 반영) < 100ms (P2P) / < 200ms (TURN) chrome://webrtc-internals
화면 프레임레이트 15-30fps (네트워크 적응) 비디오 통계
대역폭 2-5 Mbps (1080p H.264) 입력 카운터
CPU (.exe) < 15% (i5 기준) 작업 관리자
메모리 (.exe) < 200MB 작업 관리자

9. 운영 / 유지보수

9.1 일상 운영

  • 서버 모니터링: 기존 wellcoms_signal, wellcoms_turn 로그 확인
  • 세션 로그 검토: 주간으로 wp_wr_sessions 테이블 확인
  • 버전 관리: GitLab에 wp-wellcoms-remote-client 리포 운영

9.2 장애 대응

증상 원인 대응
PIN 발급 안 됨 signal 서버 다운 docker compose restart wellcoms_signal
TURN 릴레이 실패 coturn 설정 오류 / 방화벽 wellcoms_turn 로그, 3478 포트 확인
화면 안 보임 WebRTC 협상 실패 chrome://webrtc-internals, ICE 상태 확인
제어 안 됨 DataChannel 닫힘 채널 상태 로그, 재연결
exe 실행 불가 .NET 런타임 / AV 차단 .NET 8 설치 안내, AV 예외 추가

9.3 업데이트 정책

유형 방식 주기
.exe 패치 다운로드 페이지 버전 갱신 (수동) 필요 시
.exe 자동 업데이트 v1.1+ 에서 구현 주간 체크
플러그인 업데이트 WP 관리자 페이지 필요 시
시그널링 서버 Docker 이미지 rebuild 필요 시

10. 향후 확장 (v2+)

graph TD
    V1["v1.0
원격 제어 기본
(이번 구현)"] V11["v1.1
자동 업데이트
세션 녹화"] V12["v1.2
파일 전송
클립보드 동기화"] V13["v1.3
멀티 모니터
관리자 커서 시각화"] V2["v2.0
UAC 권한 제어
(서비스 모드)"] V3["v3.0
음성 통화
재부팅 후 재연결"] V1 --> V11 --> V12 --> V13 V13 --> V2 --> V3

우선순위 후보

  1. 세션 녹화 (v1.1) — 법적 보호, 품질 관리. WebRTC MediaRecorder로 mp4 저장
  2. 자동 업데이트 (v1.1) — .exe 버전 체크, 백그라운드 다운로드, 재시작
  3. 파일 전송 (v1.2) — DataChannel 'file' 추가, 드래그 앤 드롭
  4. 클립보드 동기화 (v1.2) — 텍스트만 1차, 이미지는 v2
  5. UAC 권한 (v2.0) — 서비스 모드 exe (별도 Windows Service), 관리자 권한 앱 제어
  6. 재부팅 후 재연결 (v3.0) — 서비스 + 부팅 시 자동 재연결
  7. 음성 통화 (v3.0) — WebRTC Audio Track 추가 (마이크)
  8. 세션 메모/태그 — 관리자가 메모, 티켓 시스템 연동

11. 파일 구조 (예상)

11.1 기존 플러그인 (수정)

wp-content/plugins/wellcoms-remote/
├── wellcoms-remote.php                    ← v2.0.0 → v3.0.0 (입력 오버레이, 토글, 로깅 추가)
├── templates/
│   └── page-remote.php                    ← 다운로드 섹션 추가
├── signal/
│   ├── server.js                          ← 변경 없음 (옵션: 로깅 추가)
│   ├── package.json
│   └── Dockerfile
├── includes/                              ← 신규
│   ├── class-wr-session-log.php           ← 세션 로그 DB
│   └── class-wr-rest.php                  ← REST API (버전, 다운로드)
└── assets/                                ← 신규 (입력 오버레이 CSS)
    └── css/input-overlay.css

11.2 신규 클라이언트 리포

wp-wellcoms-remote-client/                  ← GitLab 별도 리포
├── WellcomsRemote.sln                      ← Visual Studio 솔루션
├── WellcomsRemote/
│   ├── App.xaml / App.xaml.cs              ← WPF 진입점
│   ├── MainWindow.xaml / .cs               ← 메인 UI (PIN, 상태)
│   ├── ControlApprovalWindow.xaml / .cs    ← 승인 모달
│   ├── Core/
│   │   ├── SignalingClient.cs              ← WS 클라이언트 (기존 프로토콜)
│   │   ├── WebRtcPeer.cs                   ← SIPSorcery 래퍼
│   │   ├── ScreenCapturer.cs               ← GraphicsCapture 통합
│   │   ├── H264Encoder.cs                  ← MediaFoundation 인코더
│   │   ├── InputInjector.cs                ← SendInput P/Invoke
│   │   └── InputChannel.cs                 ← DataChannel 'input' 처리
│   ├── Models/
│   │   ├── InputEvent.cs                   ← JSON → C# 모델
│   │   └── SessionState.cs                 ← 상태 머신
│   └── docs/
│       └── README.md
├── WellcomsRemote.Tests/                   ← xUnit 단위 테스트
└── scripts/
    ├── publish.ps1                          ← Single-file publish 스크립트
    └── sign.ps1                            ← (향후) Trusted Signing 연동

11.3 배포 아티팩트

wellcoms-remote-v1.0.0.zip                  ← GitHub Release / WP uploads
├── WellcomsRemote.exe                      ← 단일 파일 (~30-50MB)
├── 사용법.txt                              ← 한국어 안내
└── version.txt                             ← 버전 + SHA256

12. 의사결정 기록 (ADR)

ADR-001: RustDesk 대신 기존 WebRTC 확장

  • 결정: RustDesk 화이트래블 도입하지 않고 기존 wellcoms-remote 인프라 확장
  • 이유: 기존 signal/turn/플러그 인 대부분 재사용, 관리자 UX 동일, 100% 자체 코드
  • 수정 (Momus 검토 반영): "100% 재사용"은 과장. signal 서버에 관리자 토큰 검증 로직 추가 필요 (기존 보안 구멍 수습)
  • 트레이드오프: 개발 기간 5-6주 vs RustDesk 셋업 2-4주 (하지만 통합 비용 추가)

ADR-002: C# .NET 8 선택

  • 결정: 클라이언트를 C# .NET 8 + WPF로 개발
  • 이유: Windows API 접근 용이, WPF 간단한 UI, 단일 exe 배포, 팀 학습 곡선 완만
  • 트레이드오프: AV false positive (Rust보다 높음), 파일 크기 (~30MB vs Rust ~10MB)
  • 폴백: AV 문제 심하면 Rust + libdatachannel로 전환 검토

ADR-003: 1단계 무료 배포, 2단계 Trusted Signing

  • 결정: v1은 ZIP+안내 무료 배포, v1.1+에서 Microsoft Trusted Signing 도입
  • 이유: 초기 검증 우선, $10/월 가성비로 SmartScreen 완전 제거
  • 트레이드오프: v1 사용자 마찰 (전화 안내로 완화)

ADR-004: DataChannel 'input' 단일 채널

  • 결정: 입력 이벤트를 별도 채널이 아닌 DataChannel 'input' 하나로 멀티플렉싱
  • 이유: 채널 수 최소화, 순서 보장 (ordered mode), 구현 단순
  • 트레이드오프: 입력 이벤트 폭주 시 채널 혼잡 (throttle로 완화)

ADR-005: v1은 asInvoker (UAC 미권한)

  • 결정: v1은 관리자 권한 요청하지 않음 (asInvoker 매니페스트)
  • 이유: UAC 프롬프트 회피로 UX 개선, 대부분의 일반 앱 제어 가능
  • 트레이드오행: 관리자 권한 앱(예: 작업 관리자, 일부 설정) 제어 불가 → v2 서비스 모드로 해결

ADR-006: 관리자 토큰 인증 추가 (Momus 검토 반영)

  • 결정: 시그널링 서버에 HMAC 기반 관리자 토큰 검증 추가 (§4.4)
  • 배경: 기존 signal 서버는 PIN만으로 viewer를 승인. WordPress 페이지 렌더링 시점의 wr_is_admin() 체크와 무관하게 WS 직접 연결로 우회 가능. Phase 1(view only)에서는 감수 가능했으나 Phase 3(control)에서는 critical 취약점.
  • 이유:
    • PIN 유출 + WS 직접 연결 → 무단 제어 가능 (랜섬웨어, 정보 탈취 위험)
    • 관리자 토큰 (HMAC 서명, 5분 만료, nonce)으로 WS 레벨 인증 추가
    • 고객 .exe에서 "어떤 관리자가 연결했는지" 표시 가능 (승인 프롬프트 신뢰성 향상)
  • 트레이드오프:
    • signal 서버 코드 변경 (약 50줄 추가)
    • docker-compose에 WR_SECRET 환경변수 추가
    • WP 플러그인에서 토큰 발급 로직 추가
    • 기존 Phase 1 브라우저 viewer와 3개월 호환 모드 유지
  • 대안 검토:
    • JWT (JSON Web Token) — 표준이지만 의존성 추가. HMAC 직접 구현이 더 단순
    • WS 연결 시 WP 쿠키 검증 — WP와 signal이 다른 컨테이너라 세션 공유 복잡
    • mTLS — 오버킬, 관리자 브라우저에 인증서 설치 불가

ADR-007: Week 0 스파이크 의무화

  • 결정: 본 개발 착수 전 3-5일간 기술 검증 스파이크 수행 (§6 Week 0)
  • 배경: SIPSorcery의 비디오+DataChannel 동시 처리 능력, MediaFoundation H.264 인코딩 성능, SendInput 정확도를 사전 검증하지 않으면 Week 2-3에 재앙 발생 가능
  • Go/No-Go 기준: 4개 스파이크 모두 PASS. 1개라도 FAIL → 대안 기술 검토 (Rust 전환 등)
  • 트레이드오프: 개발 기간 +1주 (5-6주 → 6-7주), 하지만 리스크 대폭 감소

13. 참조

  • 기존 개발문서: 웹화면뷰어-개발.md (Phase 1-2 완료 내역)
  • 공개 소개서: 웹화면뷰어-개발-공개.md
  • 워드프레스 플러그인: data/wp-content/plugins/wellcoms-remote/
  • 시그널링 서버: signal/server.js (관리자 토큰 검증 추가 — §3.3, §4.4 참조)
  • 관련 GitLab: gitlab.wellcoms.co.kr/jskim/wp-wellcoms-remote (기존)

외부 기술 참조


부록 A: 핵심 용어

용어 정의
WebRTC 브라우저/앱 간 P2P 실시간 통신 규격. 비디오/오디오/데이터 전송
DataChannel WebRTC의 신뢰성 있는 양방향 메시지 채널 (TCP 유사)
ICE / STUN / TURN NAT 통과를 위한 후보 수집/릴레이 프로토콜
SDP / Offer / Answer WebRTC 연결 협상 메시지
SendInput Windows API, 마우스/키보드 이벤트를 시스템 큐에 주입
Windows.Graphics.Capture Win10 1903+ 공식 화면 캡처 API
SmartScreen Windows의 다운로드 파일 평판 검사 기능
MOTW Mark of the Web, 인터넷에서 다운로드된 파일에 붙는 메타데이터
PE 헤더 Windows 실행 파일 메타데이터 (버전, 회사명 등)