IoT 보안의 끝판왕: 내 뇌파 데이터가 오픈된 MQTT 브로커로 전송된다면
최근 Hacker News와 기술 블로그들을 뜨겁게 달군 글이 하나 있습니다. 제목부터 자극적인데요, “My smart sleep mask broadcasts users’ brainwaves to an open MQTT broker” 입니다. 15년 넘게 엔지니어링 밥을 먹으며 별의별 보안 사고를 다 봤지만, 이번 건은 정말 “기가 찬다”는 말이 절로 나옵니다.
단순히 개인정보가 털린 수준이 아닙니다. 물리적인 신체 자극(전기 충격)을 제어하는 권한까지 노출되었습니다. 오늘은 이 사건을 기술적인 관점에서 뜯어보고, 시니어 엔지니어로서 우리가 무엇을 경계해야 하는지 이야기해 보겠습니다.
사건의 발단: 킥스타터 제품의 배신
한 개발자(aimilios)가 킥스타터에서 ‘스마트 수면 마스크’를 구매했습니다. EEG(뇌파) 모니터링, 눈 주변 근육 자극(EMS), 진동, 온열 기능까지 갖춘 꽤 그럴듯한 하드웨어였습니다. 하지만 앱 연결이 불안정하자, 이 개발자는 “그냥 내가 직접 제어 패널을 만들어야겠다” 고 결심합니다. 그리고 요즘 트렌드답게 Claude(LLM) 에게 리버스 엔지니어링을 시킵니다.
1. Bluetooth (BLE) 스캐닝의 실패
첫 시도는 BLE 패킷 분석이었습니다. 35개의 주변 기기 중 마스크를 찾아내고 데이터 채널을 매핑했죠. 하지만 프로토콜이 비표준이었습니다. Modbus, JSON, Raw Bytes 등 온갖 패턴을 대입해 봐도 장치는 묵묵부답이었습니다. 보통 여기서 포기하거나 오실로스코프를 꺼내 들기 마련인데, 여기서 방향을 틉니다.
2. Flutter 앱 뜯어보기 (신의 한 수)
안드로이드 APK를 디컴파일했습니다. 문제는 이 앱이 Flutter 로 만들어졌다는 점입니다. Flutter는 Dart 코드를 네이티브 ARM64 바이너리로 컴파일하기 때문에, 일반적인 Java/Kotlin 안드로이드 앱처럼 소스 코드를 복원하기가 매우 까다롭습니다. 비즈니스 로직이 거대한 바이너리 블롭(Blob) 덩어리로 뭉쳐져 있죠.
하지만 여기서 기본 중의 기본 인 strings 명령어가 등장합니다. 바이너리 안에 하드코딩된 문자열을 뒤져본 것이죠. 결과는 충격적이었습니다.
- MQTT 브로커 자격 증명 (Credentials): 모든 앱 사본이 공유하는 공용 계정
- 클라우드 API 엔드포인트
- 프로토콜 디버그 메시지 (패킷 구조 노출)
Flutter의 난독화(또는 컴파일 구조)를 믿고 가장 기본적인 보안 수칙인 “자격 증명 하드코딩 금지” 를 어긴 것입니다. 심지어 blutter라는 Flutter 전용 디컴파일 툴을 사용해 Dart 함수 이름과 커맨드 바이트까지 전부 읽어냈습니다.
기술적 재앙: MQTT의 잘못된 사용
가장 끔찍한 부분은 여기입니다. 알아낸 자격 증명으로 제조사의 MQTT 브로커에 접속해 봤더니, 내 기기뿐만 아니라 전 세계에서 활성화된 모든 기기의 데이터가 쏟아져 들어오기 시작했습니다.
- Topic 격리가 전혀 안 됨: 누구나
subscribe하면 남의 데이터를 볼 수 있습니다. - 데이터 내용: 실시간 EEG(뇌파) 데이터, 수면 단계(REM, Deep Sleep), 배터리 상태.
- 제어 권한: 단순히 읽기만 가능한 게 아니었습니다. EMS(전기 근육 자극) 명령을 보내면, 지구 반대편에서 자고 있는 사람의 눈가에 전기 충격을 줄 수 있는 상태 였습니다.
엔지니어로서 이 아키텍처를 보면 한숨만 나옵니다. IoT에서 MQTT는 표준처럼 쓰이지만, 기기별 인증(Client Certificate)이나 ACL(Access Control List) 설정은 선택이 아니라 필수입니다. 이걸 킥스타터 제품 특유의 “일단 출시하고 보자”는 마인드로 건너뛴 결과가 이겁니다.
Hacker News의 반응과 시사점
이 글이 올라오자 Hacker News 커뮤니티도 난리가 났습니다. 몇 가지 인상적인 코멘트를 공유합니다.
- “S in IoT stands for Security”: IoT의 ‘S’는 보안을 뜻한다(즉, 보안이 없다)는 뼈 있는 농담이 다시 한번 증명되었습니다.
- 킥스타터의 현실: “산업 디자이너들이 엔지니어링을 ‘돈 주면 되는 싼 용역’ 정도로 생각하고 덤벼들었다가 망하는 케이스”라는 지적이 있었습니다. 겉만 번지르르하고 속은 썩어있는 제품이 양산되는 이유죠.
- LLM과 보안: 글쓴이는 이 모든 과정을 Claude에게 시켜서 30분 만에 끝냈습니다. 이제 전문 해커가 아니더라도, LLM을 낀 호기심 많은 개발자가 제조사의 보안 허점을 순식간에 털어버릴 수 있는 시대가 되었습니다.
Principal Engineer의 시선: 우리는 무엇을 배워야 하나
1. 하드코딩은 죄악입니다.
아무리 바이너리로 컴파일된다 해도, strings 명령어 한 방이면 다 뚫립니다. API Key나 MQTT Credential을 앱 내부에 박아넣는 건 “제발 해킹해주세요”라고 광고하는 꼴입니다. 최소한 토큰 교환 방식이나 기기별 고유 인증서를 써야 합니다.
2. Flutter가 보안을 보장해주지 않습니다.
Flutter/Dart의 바이너리 구조가 복잡하다고 해서 안심하면 안 됩니다. blutter 같은 툴은 계속 진화하고 있고, LLM은 어셈블리 분석의 진입 장벽을 낮추고 있습니다. Security by Obscurity(은폐를 통한 보안)는 더 이상 유효하지 않습니다.
3. IoT 기기 격리 (Network Isolation) 사용자 입장에서는 이런 기기를 내 메인 네트워크에 붙이는 건 자살행위입니다. 집안에 IoT 기기를 들인다면 반드시 Guest Network나 별도의 VLAN으로 격리해야 합니다. 내 뇌파가 공공재가 되는 꼴을 보고 싶지 않다면 말이죠.
결론:
기술은 점점 발전하지만, 기본을 지키지 않는 엔지니어링은 더 큰 재앙을 불러옵니다. 이번 사건은 “기능 구현” 에만 급급해 “보안 아키텍처” 를 무시했을 때 어떤 일이 벌어지는지 보여주는 완벽한 반면교사입니다. 여러분이 만드는 제품은 과연 안전한가요? 오늘 당장 코드에 하드코딩된 키가 없는지 grep 한번 돌려보시길 권합니다.