MQTT로 카톡서버 만들어보자 - 6/10. 입구컷 안 당하기

빙고리우스·2024년 3월 29일
4
post-thumbnail

누군가 당신의 대화를 엿듣는다면?

보안에는 4단계가 있다(고하더라).

  • 네트워크 수준 보안
  • 전송 수준 보안
  • 애플리케이션 수준 보안
  • 페이로드 암호화

일단 영어가 섞여 있으니 용어가 어질어질하다.

#단계보안 방법
1네트워크 수준 보안VPN 터널을 사용하여 네트워크 연결 보호
2전송 수준 보안HTTP와 마찬가지로 TLS/SSL로 전송 계층 보호
3애플리케이션 수준 보안고유한 클라이언트 식별자, 또는 이름/비밀번호로 자격 증명
4페이로드 암호화애플리케이션 수준에서 페이로드 자체를 암호화

아무리 못해도 Web만큼은 해줘야하지 않겠니?

그래서 딱 2개, 2번 TLS/SSL 보안과 3번 자격증명 해보자.
나머지는 이 글 범위를 넘어선다(라고 쓰고 줄행랑💨💨💨).

자격증명 ▶ 민쯩 까봐!

맨 먼저 내가 누군지 확인 안 된 놈은 입구컷이다.
그래야 어장 관리가 되지 안 그러면 broker 거덜난다.
mosquitto는 기본적으로 localhost 로 붙을 땐 자격증명 없어도 붙지만, 외부에서 붙을 땐 자격증명이 필요하다.
localhost고 뭐고 무조건 내가 누군지 까야 붙을 수 있게 하려면 mosquitto.conf 파일에 다음과 같이 하면 된다.

그리고 mosquitto 2.0 이상에서는 localhost가 아닌 다른 곳에서도 붙게 하려면 listener를 정의해야 한다. 보안 때문에 강화된 거란다.

mosquitto.conf

listener 1883
allow_anonymous false

그 다음 입장자 명단을 만들고, 해킹을 대비해 암호화 한다.
password

server:pwd
client:pwd

이 파일은 mosquitto_passwd 프로그램으로 다음의 명령으로 암호화하라.

$> mosquitto_passwd.exe password

그러면 파일이 아래와 같이 변하는 기적을 맛볼 것이다.

password

server:$7$101$p4wSJs3pD1lwGE2X$HeddQkc56jh2ZehHeCe/W+dq3EgynumOmMdYTkw8hdu8rKXbOjWPfYiqmM/wmTh3oIyu6fcVyH4w84E/SRIMjw==
client:$7$101$QZizFrfQBO53ZAcq$J6GR7oh5UqeK3UGOVy7EpG0Sf7TIJObl6pkRTyjA8j4xy3jdbS0kJEjOSt7IQ6K5tsqpgwHkmubbxKf0gg97/w==

broker(mosquitto) 설정

password 파일을 만들었으면 준비는 다 된거다.
이 파일을 mosquitto.conf 파일에 추가하고 함 껏다 켜준다.

mosquitto.conf

listener 1883
allow_anonymous false
password_file C:\Program Files\mosquitto\conf.d\password

이제 우리는 정문에 기도를 세워 둔거다.
기본 어장 관리는 된다고 보면 된다.

server.js, client.js 설정

client 입장에서는, 이제 물관리하는 이 아저씨를 통과해야 한다.

당연히 민증 까야지.

server.js

const options = {
    host: 'stark',
    port: 1883,
    username: 'server',
    password: 'pwd'
}
const client = mqtt.connect(options);

server도 borker 입장에서는 client다.

client.js

const options = {
    host: 'stark',
    port: 1883,
    username: 'client',
    password: 'pwd'
}
const client = mqtt.connect(options);

'stark'가 뭐냐구?
hosts 파일에 등록한 mosquitto 서버 이름이다.

C:\Windows\System32\drivers\etc\hosts

192.168.219.101 stark

이름이 왜 stark 냐구? mosquitto example 에 있는 이름 그대로 썼다.

TLS/SSL 암호화


네트웍 중간에 김선생이 청진기 대고 있으면 어떡할건가?

다음은 wireshark으로 패킷 캡쳐한 것이다.
암호화하지 않았을 경우 server/login 으로 넘긴 데이터가 다 보인다.
뜨악😱이다.

이 상태로 플젝 끝했다가는 개폭망이라 봐도 무방하다.

김선생 못 알아보게 만들어야 한다.
다음은 TLS로 암호화한 것이다.
뚫어져라봐도 당췌 뭐가 오고 간 것인지 추측도 하기 어렵지 아니한가 우하하하.

심지어 topic명도 알아 볼 수 없다.

이거 해보자 했는데, 이미 한 분들 많더라.😂
인증서 만드는 것은 이 글을 따라하면 된다.
다만 nodejs 관련 설정은 다음과 같이 하면 된다.

server.js

const fs = require('fs')
const path = require('path')
const KEY = fs.readFileSync(path.join(__dirname, '/tls/server.key'))
const CERT = fs.readFileSync(path.join(__dirname, '/tls/server.crt'))
const TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, '/tls/ca.crt'))

const options = {
    port: 1883,
    host: 'stark',
    username: 'server',
    password: 'pwd',
    key: KEY,
    cert: CERT,
    rejectUnauthorized: true,
    ca: TRUSTED_CA_LIST,
    protocol: 'mqtts'
}

const server = mqtt.connect(options);

client.js

const fs = require('fs')
const path = require('path')
const KEY = fs.readFileSync(path.join(__dirname, '/tls/client.key'))
const CERT = fs.readFileSync(path.join(__dirname, '/tls/client.crt'))
const TRUSTED_CA_LIST = fs.readFileSync(path.join(__dirname, '/tls/ca.crt'))

const options = {
    port: 1883,
    host: 'stark',
    username: 'client',
    password: 'pwd',
    key: KEY,
    cert: CERT,
    rejectUnauthorized: true,
    ca: TRUSTED_CA_LIST,
    protocol: 'mqtts'
}

const server = mqtt.connect(options);

사실 보안을 다룰 때 '완벽' 이란 없다.
외부 침투를 아무리 완벽하게 막았다고 하더라도, 대부분 구멍은 내부자에게 있다.
스노든 아저씨를 봐라.

따라서 자격증명 파일, 인증서 파일은 은밀한 곳에 따로 보관하자.
적어도 production 환경이라도...🙏🙏🙏


기도: 기도는 일본말 木戸:きど(gido)로 우리말은 문지기쯤 되지 않을까. 영어로는 Bouncer라고 한다. 등판에 security 라고 쓴 미드도 있긴 하더라.
profile
다할줄아는 사람보다 뭔가 한가지 똑부러지게하는 사람이되자.

0개의 댓글