개발 취재록

Windows 로컬 웹앱에서 WiFi QR 자동 입력값이 null로 나올 때

무용 연습용 영상·대형 동기화 웹앱(formation)의 로컬 접속 화면을 다듬고 있었다. Windows PC의 Node 서버에서 현재 WiFi 이름과 비밀번호를 읽어 WiFi 접속용 QR의 입력값을 채우려 했지만, 첫 검증에서 /wifi 응답은 null이었다.

결론

이 사례에서 선택한 방법은 브라우저가 아니라 로컬 Node 서버에서 WiFi 정보를 읽는 것이었다. netsh wlan show interfaces로 현재 SSID(무선 네트워크 이름)를 읽고, netsh wlan export profile ... key=clear로 내보낸 XML의 <keyMaterial>에서 비밀번호를 얻어 /wifi로 전달하는 구조다. 아래 명령의 ...는 생략 표시이며, 그대로 실행할 완성 명령이나 실제 구현 코드는 기록에 없다.

null이 나오면 먼저 서버 PC가 WiFi에 연결돼 있는지 확인한다. 당시 확인 보고는 WiFi 연결이 끊겼고 PC가 유선을 사용하고 있다는 내용이었다. 연결된 상태에서 SSID·비밀번호가 채워지는지는 확인 못 했다. 연결이 없다는 설명은 가능하지만, 그것만으로 파싱 코드가 정상이라고 입증되지는 않는다.

또한 /wifi에 평문 비밀번호를 담는 설계였다. 운영자 페이지만 호출한다는 설명은 있지만, 다른 기기의 직접 요청을 서버가 차단한다는 근거는 없다. 동작 검증과 함께 접근 제한도 별도로 확인해야 한다.

상황

문제의 대상은 Windows PC에서 실행하는 로컬 서버와 운영자 화면이었다. 운영자 화면은 WiFi 정보를 입력받아 브라우저 저장소인 localStorage에 보관하고 QR을 만들었으며, 여기에 “현재 연결된 WiFi 정보를 기본값으로 쓰기”가 요청됐다. 변경 대상으로 보고된 파일은 live/server.jslive/public/server.html이다.

증상

출발점은 자동 입력 기능 요청이었다. 서버를 띄워 검증한 단계에서 /wifinull을 반환했고, 이어진 확인에서는 WiFi가 끊긴 상태라고 보고됐다. 화면 렌더링과 “현재 연결된 WiFi 불러오기” 버튼 표시는 확인됐지만, 실제 WiFi 정보가 입력됐다는 보고는 없었다.

원인

먼저 정보를 읽는 위치를 바꿔야 했다. 당시 설계 판단은 브라우저에서 WiFi 비밀번호를 직접 읽을 수 없으므로, PC에서 실행되는 Node 서버가 운영체제 명령을 호출하도록 한다는 것이었다. 이 구조가 읽으려는 대상은 서버 PC의 연결 정보다.

실제 null에 대해서는 WiFi 미연결 상태가 설명으로 제시됐다. 다만 기록에는 netsh 출력 전체나 파서 코드가 없고, State: disconnected라는 확인 요약만 있다. 당시의 “파싱 로직 자체는 정상”이라는 결론은 연결된 상태의 시험 결과로 뒷받침되지 않으므로, 이 글에서는 정상 판정으로 받아들이지 않는다.

해결

서버에서 읽고, 저장값이 없을 때 채운다

구현 설명에 나타난 흐름은 다음과 같다. 실제 코드를 재현한 예제가 아니라 당시 보고된 설계다.

  1. 서버가 netsh wlan show interfaces로 현재 SSID를 읽는다.
  2. 프로필을 key=clear 옵션으로 XML에 내보내고 <keyMaterial>에서 비밀번호를 읽는다. 번역되는 출력 문구에 덜 의존하려는 선택이었다. 한국어·영문 환경에서 각각 검증한 결과는 없다.
  3. /wifi 응답을 운영자 페이지에서 받아 저장값이 없을 때 자동 입력하고, 버튼으로 다시 불러오게 한다.

명령 자체의 조건도 구분해야 한다. Microsoft 문서는 export profilekey=clear가 로컬 관리자 실행 조건에서 키를 평문으로 내보낸다고 설명한다. 이는 명령에 대한 외부 근거이며, 당시 서버의 실행 권한을 확인해 주지는 않는다. Microsoft Learn: netsh wlan

평문이 어디까지 가는지 확인한다

이 설계에서 확인되는 노출 지점은 /wifi 응답이다. 뷰어·리더 페이지는 호출하지 않는다고 했지만, 서버의 인증이나 접속 위치 제한은 설명되지 않았다. “버튼이 없는 페이지”와 “요청해도 비밀번호를 받을 수 없는 클라이언트”는 구분해서 검증해야 한다.

XML 내보내기 뒤 파일을 어떻게 처리하는지도 기록에 없다. SSID와 프로필 이름이 다를 때의 처리, 명령 호출 방식, XML 파싱 방식, 응답 JSON 형태 역시 확인 못 했다. 따라서 이 기록만으로 복사해 쓸 서버 코드를 만들거나 평문 파일이 남지 않는다고 보증할 수는 없다.

후속 검증에서는 WiFi에 연결한 서버 PC에서 명령 결과와 /wifi 응답, 폼 입력까지 이어서 확인할 필요가 있다. 다른 기기에서 직접 호출할 수 있는지와 내보낸 파일의 처리도 확인 대상이다. 현재 기록의 도달점은 미연결 상태의 null과 화면 렌더링 확인까지다.

같은 화면에서는 WiFi QR과 뷰어 접속 QR이 가까워 카메라에 함께 들어오는 불편도 있었다. 카드 안에서 벌리는 수정 뒤에도 더 넓은 간격이 요구돼 width:100vwspace-between으로 화면 양 끝에 배치했다고 보고됐다. 이후 실제 휴대폰 스캔이 개선됐다는 확인은 없으므로, 배치 변경까지만 확인된 곁가지다.

취재 후기 — 값이 없다는 것과 코드가 맞다는 것

박도은
무용 연습용 웹앱의 로컬 접속 화면을 다듬고 있어. WiFi 접속용 QR에 쓸 이름과 비밀번호를 매번 넣는 대신, 현재 연결에서 가져오려는 거야.
정바다
입력칸을 자동으로 채우려면 읽는 위치부터 정해야겠네. 브라우저에서 비밀번호를 직접 읽는 대신, PC에서 도는 Node 서버에 맡기는 구조야.
정하늘
서버가 운영체제 명령으로 정보를 읽고 /wifi라는 요청 경로로 돌려주게 하자. 우리가 가져오려는 건 서버 PC가 연결한 WiFi 정보야.
박도현
그러면 읽는 쪽과 입력하는 쪽을 나눠 확인해야겠네. 서버 응답에 값이 있는지부터 보고, 그다음 화면의 자동 입력을 확인하자.
정바다
SSID, 즉 무선 네트워크 이름은 netsh라는 Windows 네트워크 명령으로 읽는 설계야. 비밀번호도 서버 쪽에서 가져오게 돼 있어.
정하늘
비밀번호는 XML이라는 구조화된 파일로 내보내서 keyMaterial 항목을 읽기로 했어. 번역되는 출력 문구에 덜 의존하려는 선택이지.
박도현
다만 그 선택과 검증 결과는 구분하자. 한국어와 영문 환경에서 실제로 모두 통과했다는 시험 결과까지 확보한 건 아니야.
박도은
서버를 띄워 확인한 /wifi 응답은 null, 값이 없는 상태야. 아직 이름과 비밀번호가 돌아오는 장면은 확인하지 못했어.
정바다
이 PC가 유선으로 연결돼서 현재 WiFi 정보가 없는 것 아닐까? 응답이 비었다는 것만으로 파싱 실패라고 정하기는 이르겠어.
정하늘
그럼 netsh의 실제 출력을 먼저 확인하자. WiFi 연결 자체가 있는지 알아야 값을 추출하는 단계까지 시험했는지 판단할 수 있어.
박도은
확인한 상태는 WiFi 연결 끊김이었고 PC는 유선을 사용하고 있었어. 현재 연결된 WiFi 이름이 없는 상태라는 설명과 맞아.
정하늘
여기까지는 빈 응답을 설명할 수 있어. 하지만 WiFi를 연결한 뒤 값을 읽는 시험이 없으니, 파싱이 정상이라는 판정까지 내릴 수는 없어.
박도현
입력이 없는 경우를 확인했다고 입력이 있는 경우도 통과한 건 아니지. 값이 비어 있는 이유와 값 추출의 정확성은 각각 확인하자.
박도은
화면은 오류 없이 떴고 현재 WiFi를 불러오는 버튼도 보여. 다만 버튼이 보인다는 관찰을 실제 비밀번호가 채워졌다는 결과로 바꾸면 안 돼.
정바다
자동 입력은 브라우저 저장소인 localStorage에 저장값이 없을 때 하도록 했어. 이미 값이 있으면 자동으로 덮는 대신 버튼으로 다시 읽는 흐름이야.
정하늘
이제 노출 범위도 확인하자. /wifi 응답에 평문 비밀번호가 들어가고 운영자 화면만 호출하는 구조지만, 직접 요청을 막는지는 아직 몰라.
박도현
뷰어 화면에서 호출하지 않는다는 설명만으로 접근 제한까지 확인한 셈 칠 수는 없어. 서버가 요청을 거절하는지는 따로 검증해야 해.
정바다
XML로 내보낸 뒤 파일을 어떻게 처리하는지도 확인 대상이네. 지금 확보한 설명에는 파일 정리나 실제 명령 호출 코드가 빠져 있어.
정하늘
다음 확인은 WiFi에 연결한 PC에서 응답과 입력칸을 함께 보는 거야. 다른 기기의 직접 요청과 내보낸 파일 처리도 아직 확인할 항목으로 남겨 두자.
박도현
이번에 얻은 교훈은 확인한 조건만큼만 결론을 쓰자는 거야. 연결이 없을 때 빈 값이 나온 사실로, 연결됐을 때의 성공까지 보증하지는 말자.