LabHub
배우기 러닝패스 코스

폐쇄망 현장 — 방산·국방 도메인 · 반입 매체 검역 · 이론

풀고 나서 보는 것은 이미 늦다

LabHub 에서 이어서 보기

한 줄 요약

반입 매체 검역은 아카이브를 풀기 전에 헤더 목록만 읽어 규모와 위험을 판정하는 일이고, 푼 뒤에 보는 것은 검사가 아니라 사고 조사다.

왜 이게 필요했나

폐쇄망에는 네트워크로 무엇이 들어오지 않는다. 그래서 들어오는 것은 거의 전부 사람이 들고 온다. 협력사 담당자가 이동식 매체를 들고 정문을 지나 반입 심사대에 올려놓는 순간, 그 안에는 대개 압축 파일 하나가 들어 있다. 패치, 설정, 의존 라이브러리, 설치 안내가 한 덩어리로 묶여 있다.

여기서 심사자가 가장 흔하게 하는 일은 일단 푸는 것이다. 풀어야 안이 보이니까. 그런데 아카이브를 푸는 행위 자체가 이미 파일 시스템을 건드리는 행위다. 항목 이름이 /etc/cron.d/agent-sync 라면 그 이름이 가리키는 자리에 쓰려 하고, ../../etc/profile.d/agent.sh 라면 푼 디렉터리 밖에 쓰려 한다. 항목이 심볼릭 링크라면 링크가 먼저 놓이고, 그 뒤에 오는 항목이 링크를 타고 엉뚱한 곳에 쓰인다. 특수 파일이 섞여 있으면 장치 노드가 생긴다. 이 모든 일은 "안을 보려고" 한 한 줄의 명령 안에서 일어난다.

그리고 압축 폭탄이 있다. 같은 바이트가 반복된 파일은 압축률이 수백 배까지 올라간다. 매체에 담긴 파일은 16KB인데 풀면 4MB가 되고, 그 비율을 조금만 키우면 심사대의 임시 디스크가 먼저 마른다. 디스크가 마르면 심사 자체가 중단되고, 중단된 심사는 대개 "일단 넘기고 나중에" 로 끝난다.

그래서 순서를 바꾼다. 먼저 목록을 읽고, 판정하고, 그 다음에 조건부로 푼다.

어떻게 동작하나

tar 는 아주 단순한 형식이다. 512바이트 헤더 하나에 이름, 크기, 권한 비트, 항목 종류, 링크 대상이 적혀 있고 그 뒤에 내용이 붙는다. 이 구조 덕분에 내용을 디스크에 쓰지 않고도 알 수 있는 것이 많다.

판정이 끝나면 그제야 푼다. 파이썬 3.12부터는 추출에 필터를 걸 수 있다. [PEP 706](https://peps.python.org/pep-0706/) 이 그 배경을 적어 두었는데, 요지는 extractall() 의 기본 동작이 아카이브의 메타데이터를 그대로 믿는 것이었고 그게 2007년에 보고된 취약점(CVE-2007-4559)의 자리였다는 것이다. 필터는 세 가지다. fully_trusted 는 예전 그대로, tar 는 GNU tar 를 흉내 내고, data 는 유닉스 고유 기능을 잘라 내 가장 좁게 푼다. PEP 는 기본값이 파이썬 3.12와 3.13에서는 경고와 함께 fully_trusted 로 남고 3.14부터 data 가 된다고 적는다.

여기서 눈여겨볼 대목이 하나 있다. data 필터는 절대 경로를 거절하지 않는다. PEP 의 tar 필터 설명대로 선두 슬래시를 떼고 나서 판정하므로, /etc/cron.d/agent-sync 는 오류 없이 격리 디렉터리 안의 etc/cron.d/agent-sync 로 들어온다. 실습 이미지에서 실측한 결과도 같았다. 그러니 "필터를 켰으니 안전하다" 와 "이 매체에 무엇이 들어 있었다" 는 다른 문장이다. 반입 판정서에 남겨야 하는 것은 뒤쪽이다.

매체(tar.gz) ──▶ getmembers()  ──▶ 규모·위험·압축비·형식 불일치  ──▶ 판정서                     (풀지 않음)                │                                                └─▶ data 필터로 항목별 판정 ──▶ 격리 디렉터리                                                                              거절 목록(사유 포함)

거절도 한 번에 모아야 한다. extractall() 에 필터를 넘기면 첫 거절에서 예외가 올라와 멈추고, 그 뒤에 무엇이 더 있었는지는 보이지 않는다. 항목마다 data_filter() 를 직접 불러 예외를 받아 두면 거절 항목과 사유가 한 번에 남는다. 심사자가 협력사에 돌려보낼 때 필요한 것이 바로 그 목록이다.

현장에서 만나는 모습

판정서에는 근거가 붙어야 한다. "위험하여 반려" 라고만 적힌 판정서는 다시 검증할 수 없고, 협력사는 무엇을 고쳐야 하는지 모른다. 어떤 아카이브의 어떤 해시를 어떤 보고서로 읽고 그 보고서에서 어떤 사유가 나왔는지가 한 줄로 이어져야 한다. 보고서를 다시 만들었으면 판정서의 해시도 다시 적는다 — 이 한 줄을 빠뜨려 심사를 두 번 받은 이야기가 반입 번들 실습에도 나온다.

서명이 검역을 대신하지 못한다. 같은 코스의 반입 번들 실습에서 다루는 서명 검증은 "보낸 사람이 맞다" 를 말해 준다. 보낸 사람이 맞아도 그 사람이 압축한 디렉터리에 빌드 캐시 4MB 와 개발용 심볼릭 링크가 딸려 들어올 수 있다. 서명과 구조 검사는 순서대로 둘 다 한다.

매체 자체도 관리 대상이다. 반입이 끝난 매체를 어떻게 다룰지는 기술이 아니라 절차의 문제다. 매체 소거는 [NIST SP 800-88 Rev.1](https://csrc.nist.gov/pubs/sp/800/88/r1/final) 이, 매체 보호 계열의 통제는 [NIST SP 800-53 Rev.5](https://csrc.nist.gov/pubs/sp/800/53/r5/upd1/final) 가 다룬다. 이 실습은 그 절차를 흉내 내지 않고, 검역 기록이 그 절차에 넘길 수 있는 모양인지까지만 본다.

한 번 당한 것. 심사대에서 아카이브를 풀어 본 뒤 "별것 없다" 고 판단하고 넘긴 매체가 있었다. 나중에 보니 그 안에 있던 docs/notes.txt 가 텍스트가 아니었다. 이름이 확장자와 맞는지를 사람이 눈으로 볼 방법은 없다. 그때부터 검역기가 앞머리 바이트를 읽는다.

다음 실습에서 할 것

매체 두 개를 합성으로 만들어 접수 대장부터 적고, 풀지 않고 목록만 읽어 규모를 재고, 위험 항목을 다섯 갈래로 나누고, 압축비로 폭탄을 가리고, 확장자와 실제 형식의 불일치를 찾는다. 그다음 data 필터로 항목마다 판정해 격리 디렉터리에 풀고 거절 목록을 남긴 뒤, 근거 해시를 붙인 반입 판정서를 쓴다. 마지막으로 같은 절차를 두 번째 매체에 그대로 돌려 판정이 뒤집히는지 본다.