← 전체 글
네트워킹/약 13분 읽기/— 조회

Outstation에서 스스로를 잠가버리지 않고 DNP3 보안 인증을 켜는 법

DNP3 SAv5 시운전: critical 기능 지정, 느린 회선의 challenge-response와 aggressive mode, update·session key.

네트워킹SCADA문제 해결시운전Security

열린 링크보다 더 나쁜 실패

DNP3 보안 인증을 잘못 켜면 깔끔한 오류가 나지 않는다. Outstation은 여전히 잘 읽힌다 — 무결성 폴, 이벤트, 모든 데이터가 흐른다 — 운전원이 차단기를 트립하려는 순간, Operate가 인증에 실패해 조용히 무산될 때까지는. 이제 모든 화면에서는 멀쩡해 보이는데 실제로는 쇠붙이를 움직이지 못하는 제어 포인트를 하나 갖게 됐다. 이게 SAv5의 특유한 함정이고, 내가 인증을 시운전할 때 폴이 아니라 제어 경로부터 확인하는 이유다.

DNP3 보안 인증은 IEEE 1815-2012(버전 5, "SAv5")에 정의돼 있다. 암호화가 아니다. 페이로드를 숨기는 일은 전혀 하지 않는다 — 스니퍼를 가진 사람은 여전히 모든 측정값을 평문으로 읽는다. 하는 일은 critical 요청이 실제로 공유 키를 가진 쪽에서 왔음을 증명하는 것이다. 그래서 위조되거나 재전송된 Operate가 outstation에 명령을 밀어넣지 못한다. 기밀성까지 필요하면 그건 아래 전송 계층의 TLS(1815 over TLS)이고, 별개의 결정이다.

키를 만지기 전에 "critical"의 의미부터 정하라

SAv5는 모든 걸 인증하지 않는다. 함수 코드의 일부를 critical로 지정하면, 그 함수만 challenge를 받는다. 나머지는 예전처럼 인증 없이 통과한다. 이 목록이 당신이 내리는 가장 중요한 설정 선택이고, 대부분의 통합업체는 기본값을 그대로 두는 바람에 이걸 틀린다.

읽기는 거의 절대 critical이 아니다 — Class 0/1/2/3 폴은 데이터를 한 방향으로 옮길 뿐 해를 끼칠 수 없다. critical로 지정할 가치가 있는 건 상태나 장치의 동작을 바꾸는 기능들이다:

  • Operate / Direct Operate(함수 4, 5) — 뻔한 것, 차단기를 움직인다.
  • Write(함수 2) — 아날로그 출력 설정값, 데드밴드, 공격자가 재조정할 수 있는 모든 것.
  • Cold/Warm Restart(13, 14), Enable/Disable Unsolicited(20, 21).
  • 운영상 의존한다면 시각 동기화.

사람들이 놓치는 미묘한 것 하나: Select(함수 3)는 그 자체로는 아무것도 동작시키지 않으니, non-critical로 두고 Operate만 challenge하고 싶어진다. 그러지 마라. Select가 인증되지 않으면, 공격자가 엉뚱한 출력 포인트를 무장시켜 정당한 운전원의 Operate를 그 위에 태울 수 있다. select-before-operate 교환 전체를 인증하거나, 아예 하지 마라.

Challenge-response는 왕복 한 번을 쓰고, aggressive mode는 그걸 되사온다

기본 메커니즘은 challenge-response이고 방향이 중요하다. 마스터가 critical 요청을 보내면 outstation이 challenge를 되돌려 보내고(object group 120 인증 객체), 마스터는 현재 session key로 challenge 데이터와 원래 요청에 대한 HMAC를 계산해 reply를 보낸다. Outstation은 동작하기 전에 MAC를 검증한다. 그래서 Operate 하나가 이렇게 된다: 요청 → challenge → reply → 응답. 두 메시지가 아니라 네 메시지다.

이더넷에서는 아무도 눈치채지 못한다. 그런데 시골 리클로저 40대를 먹이는 1200baud 무선 링크에서는 그 추가 왕복이 실제로 느껴진다. 수십 바이트짜리 challenge와 reply, 거기에 링크 계층 confirm까지, 반이중에 턴어라운드 지연이 있는 느린 경로에서는 제어 지연이 두 배로 늘어나는 걸 지켜볼 수 있다.

그럴 때 쓰는 게 aggressive mode다. 요청 측이 인증 데이터를 critical 요청과 같은 메시지에 실어 보내는데, 마지막으로 받은 challenge의 challenge sequence number(CSQ)를 사용한다. 추가 왕복이 없다. 대가는 aggressive mode가 이전에 교환된 challenge에 기댄다는 점이라, 그와 무관하게 얼마나 자주 새 challenge를 강제할지 설정한다. 나는 대역폭이 빠듯한 직렬·무선에는 aggressive mode를, 왕복이 공짜인 이더넷에는 평범한 challenge-response를 쓴다 — 사고 상황에서 따져볼 게 하나 줄어들기 때문이다.

Update key와 session key는 같은 키가 아니다

여기서 시운전이 어긋난다. 두 개의 다른 키가 작동하는데 사람들이 이걸 뭉뚱그리기 때문이다.

Update key는 수명이 길고 사용자별이다. 대칭으로 사전 공유하거나(마스터와 outstation에 같은 키를 입력) 인증서로 비대칭 분배한다. 거의 바뀌지 않는다. 유일한 임무는 session key의 주기적 인계를 보호하는 것이다.

Session key는 수명이 짧은 대칭 키로, 실제로 트래픽에 MAC를 건다. 회전은 outstation이 주도한다 — 시간 간격이나 메시지 수 중 먼저 도달하는 쪽에서 바꾼다. 회전할 때 새 session key는 update key로 감싸져서 스니퍼가 낚아채지 못한다. 그래서 "오버헤드를 줄이려고" session key 수명을 편하게 길게 잡을 수 없다 — 수명 짧은 session key가 핵심 전부이고, update key는 절대 평문으로 회선을 타지 않는다.

시운전에서의 실질적 결과: update key를 한쪽에 잘못 입력해도 즉시 실패하지 않는다. 초기 링크는 올라온다. 읽기도 된다. 그러다 첫 session key 변경이 인증에 실패하고, 그 시점부터 critical 요청이 거부되기 시작한다. 그래서 잘못된 키는 지연되고 간헐적인 제어 실패처럼 보인다 — 현장 기술자에게 넘겨줄 수 있는 최악의 증상이다. Update key는 데이터가 흐르는 걸 보고 검증하지 말고, session key 변경을 강제로 일으켜 성공하는 걸 보고 검증하라.

SAv5는 시계 동기화를 요구하지 않는다

TLS에서 넘어온 사람들이 놀라니 분명히 말해둔다: DNP3 SA는 시간에 의존하지 않는다. 재전송 방지는 유효 시간창 안의 타임스탬프가 아니라 단조 증가하는 challenge sequence number에서 나온다. 그래서 시계가 틀렸거나 설정되지 않은 outstation에서도 인증을 시운전할 수 있고, 여전히 동작한다. 제어가 인증에 실패한다고 시각 동기화 문제를 쫓지 마라 — 이 프로토콜에서 둘은 무관하다. (이벤트 타임스탬프를 위해 시각 동기화는 여전히 원할 수 있다. 다만 SA를 위해서는 아니다.)

"된다"가 아니라 보안 통계를 읽어라

SAv5는 보안 통계를 유지하고 보안 통계 이벤트(object group 121, 122)를 낸다 — 인증 실패, 예상치 못한 메시지, MAC 실패, 키 변경 이벤트 등등. 시운전 중에는 이것들이 진짜 피드백이고, 데이터가 흐르는지 노려보는 것보다 낫다. 이걸 폴하면 "outstation이 challenge를 한 번도 안 했다"(critical로 지정한 게 없다)와 "challenge했는데 내 reply가 MAC에 실패했다"(키나 알고리즘 불일치)를 구분할 수 있다.

MAC가 계속 실패할 때 점검할 불일치 몇 가지:

  • MAC 알고리즘 — 양쪽이 HMAC 알고리즘과 절단 길이에 합의해야 한다. HMAC-SHA-256을 16바이트로 절단해 제시하는 마스터와 다른 절단을 기대하는 outstation은 그냥 검증되지 않는다.
  • 사용자 번호 — 각 사용자는 자신의 update key를 가진다. 마스터와 outstation은 같은 키에 대해 같은 사용자를 참조해야 한다. 기본 사용자 1이면 괜찮지만, 누군가 여러 사용자를 프로비저닝했다면 여기서 한 칸 어긋나면 조용히 실패한다.
  • 일치하지 않는 critical 함수 목록 — 마스터는 Write를 critical로 보는데 outstation은 아니라면(또는 그 반대), challenge/authenticate 핸드셰이크가 어긋난다.

출장 한 번을 아껴주는 시운전 순서

이 순서대로 하면 각 실수를 값쌀 때 잡는다:

  1. SA를 설정한 채로 링크를 올리되 평범한 읽기가 여전히 되는지 확인하라 — 주소 지정과 전송이 정상임을 증명한다.
  2. session key 변경을 강제하고 인증되는지 확인하라 — 이게 진짜 update key 점검이고, 키가 틀렸다면 일찍 실패한다.
  3. 인증된 제어를 처음부터 끝까지 한 번 내려보라 — 예비 포인트나 테스트 포인트를 select하고 operate한 뒤, Operate가 응답을 받았다는 것만이 아니라 outstation이 실제로 동작했음을 확인하라. 이게 바로 조용히 망가지는 경로라서, 물리적으로 증명해야 하는 그 하나다.
  4. 이 세 가지가 끝난 다음에만: 링크가 필요로 하면 aggressive mode를 켜고, 제어가 여전히 동작하는지 다시 확인하라.

이렇게까지 신중해야 하는 이유가 첫 문단의 함정이다. 완벽하게 읽히면서 제어를 거부하는 outstation은 어떤 대충의 점검도 통과하고, 정작 중요한 그 한순간에 실패한다. 현장을 떠나기 전에 인증 하에서 제어 경로를 증명하라. 안 그러면 진짜 개폐 작업 도중에 알게 되니까.