블록체인 IoT 통합: MQTT 디바이스 인증과 데이터 무결성 앵커링
블록체인과 IoT를 결합해 MQTT 메시지의 디바이스 인증, 데이터 무결성, 감사 추적을 설계하는 하이브리드 아키텍처를 정리한다.
2026-08-14 · 최초 발행 2025-10-31
MQTT 파이프라인에 감사 가능한 무결성을 더하는 방식
IoT 환경에서는 이기종 디바이스가 지속적으로 데이터를 보내고, 엣지 구간의 신뢰 수준도 제각각이다. 중앙형 인증과 저장만으로 운영할 수는 있지만, 사후 감사나 데이터 위·변조 검증이 필요해지는 순간에는 별도의 신뢰 경로가 필요하다.
블록체인은 이 경로를 만드는 데 쓸 수 있다. 다만 센서 원본을 모두 온체인에 기록하면 지연과 비용 부담이 커진다. 실무에서는 원본을 오프체인에 보관하고 데이터 해시 또는 머클 루트만 체인에 커밋하는 하이브리드 구성이 중심이 된다. MQTT 브로커와 블록체인 노드 사이에는 서명 검증, 중복 방지, 배치, 재시도, 모니터링을 수행하는 게이트웨이를 둔다.
디바이스 식별자는 공개키 기반으로 정의할 수 있으며, DID(Decentralized Identifier)와 VC(Verifiable Credential)를 적용할 수도 있다. 이때 키 회전, 폐기, 권한 위임까지 함께 관리해야 한다.
신뢰 경계를 나누는 구성
디바이스의 개인키는 보안칩(SE/TPM) 또는 HSM에 보관하고, 제조 시점에 루트 CA 또는 DID Registry에 등록한다. 키 회전·폐기·소유권 이전은 별도 절차로 자동화한다. 인증은 TLS 클라이언트 인증, MQTT 토픽 ACL, 메시지 수준 서명(ECDSA/Ed25519)을 병행한다.
데이터 무결성은 메시지의 요약 해시를 만들고, N개 단위의 머클 트리 루트를 배치로 커밋하는 방식으로 확보한다. 원본 데이터는 WORM이나 버전 관리를 사용하는 객체 스토리지에 불변 저장한다. 재현 가능한 직렬화를 위해 CBOR 또는 Protobuf와 필드 정렬을 적용하고, 타임스탬프와 논스를 포함해 재사용·재주입 공격을 막는다.
스마트 컨트랙트는 디바이스 등록과 폐기, 루트 커밋(anchor), 감사 이벤트 CommitLogged를 담당한다. Ownable 또는 AccessControl로 접근을 제한하고, 저장은 최소화하며 이벤트 중심으로 설계한다. 개인정보는 오프체인에서 암호화해 보관한다.
메시지가 앵커링되기까지
입력은 TLS로 보호된 MQTT 메시지(JSON/CBOR)와 디바이스 클라이언트 인증서 또는 DID 증명이다. 메시지에는 deviceId, ts, nonce, payload, signature가 포함된다.
게이트웨이는 서명과 시계 오차, 논스 중복을 확인한 뒤 해시를 생성한다. 검증을 통과한 메시지는 머클 배치에 쌓이고, 루트는 L2 트랜잭션으로 제출된다. 트랜잭션 영수증은 별도로 보관한다. 최종적으로 온체인에는 txHash, blockNumber, merkleRoot가 남고, 오프체인 저장소의 URL 또는 CID와 감사 인덱스, 검증 API가 이를 연결한다.
센서·물류·정산 데이터의 검증
산업 설비에서는 배치 생산 라인의 센서 데이터 해시를 1분 단위 머클 루트로 커밋할 수 있다. 불량이 발생하면 해당 배치의 루트와 리프 증명으로 원본 데이터의 무결성을 입증한다.
콜드체인에서는 냉장차 IoT 게이트웨이가 MQTT로 온도 데이터를 전송한다. 이동통신이 불안정하면 로컬 버퍼에 보관했다가 동기화하고, 인도 시점에는 검증 API를 통해 온체인 커밋과 리프 증명을 제출해 계약 분쟁을 줄인다.
AMI 스마트 미터링에서는 장비가 검침값을 서명해 전송하고, 유틸리티 게이트웨이가 월·일 단위로 배치 커밋한다. 규제 감사 시에는 온체인 이벤트와 오프체인 원본의 해시 일치성을 검증한다.
중앙형 저장과 온체인 기록의 선택 기준
| 아키텍처 | 성능 | 확장성 | 일관성 | 안정성 | 운영 편의 |
|---|---|---|---|---|---|
| 중앙형 IoT 플랫폼 | 매우 높음 | 수평 확장 용이 | 중앙 DB 기준 강한 일관성 | 단일 장애점 존재 | 운영 단순 |
| 온체인 직접 기록 | 낮음(지연 큼) | 네트워크 한계 종속 | 강한 감사 추적 | 탈중앙 내고장성 | 키·가스 관리 부담 |
| 하이브리드(오프체인+온체인 해시) | 높음 | 배치·롤업으로 매우 높음 | 무결성 강함(머클 증명) | 온·오프 이중화 가능 | 브리지 운영 복잡성 존재 |
MQTT 메시지를 배치 커밋하는 Node.js 예시
전제조건은 다음과 같다.
- Node.js 18+, npm
- 라이브러리: mqtt, ethers@6
- 이더리움 L2/테스트넷 RPC, 프라이빗 키(게이트웨이 서명용)
- 배포된 간단한 컨트랙트 ABI: function anchor(bytes32 root, uint256 batchId) external
디바이스는 Ethereum Signed Message 규격으로 payload 해시를 서명(secp256k1)하며, 메시지 스키마은 {deviceId, ts, nonce, payload, sig}라고 가정한다.
npm i mqtt ethers
// index.js
import mqtt from "mqtt";
import { ethers } from "ethers";
const MQTT_URL = "mqtts://broker.example.com:8883";
const TOPIC = "sensors/+/data";
const RPC_URL = process.env.RPC_URL;
const PRIV_KEY = process.env.PRIV_KEY; // 게이트웨이 계정
const CONTRACT_ADDR = process.env.CONTRACT_ADDR;
const ABI = [
"function anchor(bytes32 root, uint256 batchId) external",
"event CommitLogged(bytes32 indexed root, uint256 batchId)",
];
const provider = new ethers.JsonRpcProvider(RPC_URL);
const wallet = new ethers.Wallet(PRIV_KEY, provider);
const contract = new ethers.Contract(CONTRACT_ADDR, ABI, wallet);
const client = mqtt.connect(MQTT_URL, {
clientId: "bc-gateway-1",
protocolVersion: 5,
clean: true,
rejectUnauthorized: true,
});
// 간단한 배치 큐
const batch = [];
const NONCE_CACHE = new Map(); // deviceId -> 최근 nonce
let batchId = 0;
function canonicalDigest(msg) {
// 직렬화 주의: 실제 운영은 CBOR/Protobuf + 필드 정렬 권장
const material = JSON.stringify({
deviceId: msg.deviceId,
ts: msg.ts,
nonce: msg.nonce,
payload: msg.payload,
});
return ethers.keccak256(ethers.toUtf8Bytes(material));
}
function verifySignature(msg) {
const digest = canonicalDigest(msg);
const recovered = ethers.verifyMessage(ethers.getBytes(digest), msg.sig);
// deviceId를 EOA 주소로 맵핑했음을 가정
return recovered.toLowerCase() === msg.deviceId.toLowerCase();
}
async function flushBatch() {
if (batch.length === 0) return;
// 간단 루트: 여러 해시를 다시 keccak256으로 축약
// 실제 운영은 머클 트리 사용 권장(리프 증명 제공)
const root = ethers.keccak256(ethers.concat(batch.map((b) => b.digest)));
try {
const tx = await contract.anchor(root, batchId);
const rc = await tx.wait();
console.log(`Anchored root=${root} batchId=${batchId} tx=${rc.hash}`);
batch.length = 0;
batchId += 1;
} catch (e) {
console.error("Anchor failed:", e?.message);
// 백오프 재시도 또는 대체 L2 전환 로직 필요
}
}
client.on("connect", () => {
console.log("MQTT connected");
client.subscribe(TOPIC, { qos: 1 });
// 10초마다 배치 플러시
setInterval(flushBatch, 10_000);
});
client.on("message", async (_topic, payload) => {
try {
const msg = JSON.parse(payload.toString());
// 리플레이 방지
const last = NONCE_CACHE.get(msg.deviceId) || -1;
if (msg.nonce <= last) throw new Error("Replay/nonce order error");
if (!verifySignature(msg)) throw new Error("Signature invalid");
const digest = canonicalDigest(msg);
batch.push({ deviceId: msg.deviceId, digest });
NONCE_CACHE.set(msg.deviceId, msg.nonce);
} catch (e) {
console.warn("Dropped message:", e.message);
// 필요 시 SIEM/알림 연동
}
});
처음에는 테스트넷이나 개인망에서 시작하고, 처리량이 증가하면 머클 트리 라이브러리 도입과 배치 크기 동적 조정을 검토한다. 재시도 백오프, 영구 큐(Kafka/Redis Streams), 감사 인덱스(DB)도 필요하다.
키·비용·장애 대응의 트레이드오프
개인키는 디바이스 SE/TPM에서 보호하고 제조 시 루트 체인 신뢰를 정립한다. 오프라인 등록과 공급망 무결성 검증을 함께 고려해야 한다. 비용 증가는 현장 공격 저항성 강화와 맞바꾼다.
네트워크에서는 TLS 클라이언트 인증을 적용하고, 브로커에서는 토픽 ACL을, 메시지에는 별도 서명을 적용한다. 다계층 인증은 지연을 늘릴 수 있지만 위협 모델을 완화한다.
온체인 비용은 머클 배치, 이벤트 위주 설계, L2·롤업으로 조절한다. 가스비와 환율은 변동하므로 최신 정보를 확인해야 한다. 이 구성은 지연 허용과 비용 절감의 선택이다.
PII는 오프체인에서 암호화하고 키 접근을 통제하며, 보존기간 정책을 둔다. 체인에는 해시와 인덱스처럼 최소한의 정보만 기록한다. 데이터 유용성과 노출 위험 최소화 사이의 균형이 필요하다.
가용성 측면에서는 프라이머리/세컨더리 멀티 브로커, 멀티 L2 페일오버, QoS2 기반 메시지 재전달을 설계한다. 비용 증가는 RTO/RPO 개선과 연결된다.
배치 앵커링이 만드는 운영상 변화
해시 일치 검증을 자동화하면 위·변조 탐지 시간을 분→초 단위로 단축할 수 있다. 외부 감사나 분쟁 대응 리드타임은 50% 이상 단축될 것으로 기대된다.
머클 배치를 사용하면 온체인 호출 횟수를 10^2~10^4배 줄일 수 있다. 메시지당 온체인 비용은 배치 크기 N에 반비례해 절감되며, 가스 비용과 환율은 변동하므로 최신 정보 확인이 필요하다. 배치 크기 N을 적용하면 단건 대비 1/N 수준으로 평균화된다.
재시도, 페일오버, 불변 저장소를 도입하면 데이터 손실 확률을 낮출 수 있다. 스푸핑·리플레이·재주입 위협 벡터를 차단해 보안 사고 건수 감소도 기대할 수 있다.
하이브리드 패턴으로 운영 범위를 분리한다
MQTT 중심 IoT 파이프라인에는 블록체인 앵커링을 하이브리드로 통합하는 방식이 적합하다. 디바이스 인증은 PKI/DID 기반으로, 데이터 무결성은 머클 배치와 온체인 커밋으로, 운영 제어는 게이트웨이 브리지로 나눈다.
L2, 배치, 오프체인 저장은 비용·지연·보안의 트레이드오프를 다루기 위한 표준 패턴으로 채택할 수 있다.