보안 4분 소요

허깅페이스가 뚫렸는데, 제로트러스트는 왜 아무것도 못 했나

보안 사고 보고서는 대개 비슷한 문장으로 끝납니다. “고객 데이터에는 영향이 없었으며, 저희는 보안을 최우선으로 생각합니다.” 그런데 가끔, 아주 가끔 다른 종류의 문서가 나옵니다. 자기 제품이 이 공격을 막지 못했다고 먼저 밝히는 포스트모템입니다. 허깅페이스는 AI 생태계의 사실상 표준 저장소죠. 이 허브를 둘러싼 침해 논의가 다시 불붙으면서, 그 이례적인 문서도 같이 소환됐습니다.

시작 전에 하나 솔직히 밝힙니다. 이번 주제로 커뮤니티 데이터를 훑어봤는데 최근 30일 사이에 이 사안을 정면으로 다룬 신선한 토론은 거의 잡히지 않았습니다. 그래서 이 글은 실시간 여론 중계가 아닙니다. 이미 공개된 사실관계와 구조적 문제를 다시 뜯어보는 쪽에 가깝습니다. 숫자로 온도를 재기 어려운 주제일수록 구조를 보는 편이 낫더군요.

보안 벤더가 “우리 제품은 못 막았다"고 쓴다는 것

Tailscale은 제로트러스트 네트워크 접근 제품을 파는 회사입니다. 그런 회사가 침해 대응 문서에 자사 제품이 이 경로를 차단하지 못했다고 적는 건, 마케팅으로 보면 자해에 가깝습니다. 그런데 보안 엔지니어링 쪽에서 보면 이게 문서 전체에서 제일 값어치 있는 문장입니다.

이유는 단순합니다. 보안 업계의 사고 보고서 대부분은 책임 회피의 문법으로 쓰여 있습니다. “정교한 국가 지원 공격자였다”, “제로데이였다” 같은 표현으로 자기 통제 범위 밖의 일이었다고 선을 긋습니다. 그러면 읽는 사람은 배울 게 없습니다. 반대로 “우리 제품의 위협 모델은 이 시나리오를 가정하지 않았다"고 쓰면, 독자는 자기 환경에도 같은 가정이 깔려 있는지 점검할 수 있습니다.

이 포스트모템의 가치는 사과에 있지 않습니다. 위협 모델의 경계선을 공개했다는 데 있습니다. 제품이 무엇을 막고 무엇을 안 막는지 명시하는 일, 모든 보안 제품이 해야 하지만 거의 아무도 안 합니다.

제로트러스트가 무력해지는 지점

제로트러스트라는 말은 이제 너무 많이 팔려서 의미가 흐려졌습니다. 원래 뜻은 간단합니다. “네트워크 안에 있다는 이유만으로 신뢰하지 않는다.” VPN에 접속했다고 해서 모든 내부 서버를 열어주지 말고, 요청마다 신원과 권한을 확인하라는 겁니다.

문제는 이 원칙이 방어하는 공격 유형이 한정적이라는 겁니다. 제로트러스트는 횡적 이동, 그러니까 공격자가 서버 한 대를 장악한 뒤 옆으로 번져나가는 걸 막는 데 강합니다. 반대로 원리상 어쩔 수 없이 취약한 지점도 있습니다.

하나는 정당한 자격증명으로 들어온 정당한 요청입니다. 공격자가 유효한 토큰을 훔쳐서 그 토큰이 원래 하던 일을 그대로 하면, 제로트러스트 계층에서는 구분할 방법이 없습니다. 정책상 허용된 행위니까요.

또 하나는 애플리케이션 계층의 논리 문제입니다. 제로트러스트는 “누가 어디에 접근하는가"를 통제할 뿐, “그 접근 권한으로 무엇을 하는가"까지 들여다보지는 않습니다. CI 파이프라인이 원래 저장소에 쓰기 권한을 갖고 있다면, 그 파이프라인을 장악한 공격자도 같은 권한을 씁니다.

마지막은 신뢰 자체가 목적지인 경우입니다. 여기서 진짜 문제가 드러납니다.

허깅페이스가 특별한 이유: 뚫리면 아래로 흐른다

일반 SaaS가 털리면 피해는 그 회사와 고객 데이터에서 멈춥니다. 모델 허브는 다릅니다. 거기 올라가 있는 게 실행 가능한 산출물이라서 그렇습니다.

우리가 모델을 쓰는 방식을 떠올려 보면 분명합니다. 코드에 저장소 이름을 적고, 라이브러리가 자동으로 가중치를 내려받고, 그대로 메모리에 올려 추론을 돌립니다. 이 과정에서 사람이 파일을 열어보는 단계는 없습니다. 수 기가바이트짜리 텐서 덩어리를 눈으로 검사한다는 건 애초에 불가능합니다.

pickle 기반 직렬화 포맷은 역직렬화 과정에서 임의 코드 실행이 가능하다는 게 오래전부터 알려진 문제였고, 그래서 safetensors 같은 포맷이 나왔습니다. 포맷 문제는 상당 부분 개선됐습니다. 그런데 포맷이 안전해져도 남는 질문이 있습니다. 이 파일이 내가 믿는 그 사람이 올린 그 파일이 맞는가.

이건 암호학이 아니라 신뢰 체인의 문제입니다. 허브 자체가 침해되면, 포맷이 아무리 안전해도 “내가 받은 게 원본"이라는 보장이 흔들립니다. 게다가 모델 가중치는 코드와 달리 diff를 봐도 아무것도 알 수 없습니다. 소스 코드 백도어는 리뷰로 잡힐 가능성이라도 있지만, 가중치에 심어진 이상 동작은 숫자 배열의 미세한 차이로만 존재합니다. 사람 눈으로는 절대 안 보입니다.

파이썬 패키지 사태와 무엇이 다른가

npm이나 PyPI 공급망 공격은 이제 익숙한 뉴스입니다. 그런데 모델 허브 쪽은 사정이 더 까다롭습니다.

우선 검증 난이도가 다릅니다. 악성 npm 패키지는 코드를 읽으면 잡을 수 있고, 실제로 커뮤니티가 그렇게 잡아냅니다. 모델은 읽을 수가 없습니다. 이상 동작을 확인하려면 실행해봐야 하는데, 어떤 입력에서 발현되는지 모르면 테스트도 헛돕니다.

진입 문턱도 다릅니다. 백엔드 개발자는 대체로 의존성 관리하는 습관이 몸에 배어 있습니다. 반면 모델을 받아 쓰는 사람 중 상당수는 데이터 사이언티스트나 연구자이고, 공급망 보안은 애초에 그들의 직무 훈련에 없던 항목입니다.

결정타는 중앙집중도입니다. 파이썬 생태계에는 그래도 미러와 사설 저장소 문화가 있습니다. 오픈 모델 생태계는 허깅페이스 쏠림이 훨씬 심합니다. 대안이 없다시피 한 단일 지점에 전 세계가 매일 접속합니다. 편리함의 대가로 우리 모두가 같은 곳에 베팅한 셈입니다.

그래서 뭘 해야 하나

거창한 대책은 접어두고, 지금 당장 확인할 수 있는 것부터 봅시다.

프로덕션에 나가는 모델은 리비전을 해시로 고정하는 게 기본입니다. 브랜치 이름만 적어두면 그 브랜치가 어제와 같은 내용이라는 보장이 없습니다. 패키지 버전을 lock 파일로 못 박는 것과 똑같은 발상입니다.

내려받은 가중치는 사내 저장소에 캐싱하고, 승인된 것만 배포 경로에 태우는 게 안전합니다. 매 배포마다 외부 허브를 실시간으로 호출하는 구조라면, 외부 사고가 곧바로 우리 프로덕션 사고가 됩니다.

추론 프로세스의 권한도 낮춰야 합니다. 모델을 로드하는 프로세스가 클라우드 자격증명이나 내부 네트워크에 자유롭게 접근할 수 있다면, 최악의 경우 피해 범위가 걷잡을 수 없이 커집니다. 모델이 악성인지 아닌지와 상관없이 지켜야 할 기본입니다.

마무리

이 사안에서 오래 남을 건 침해 자체가 아닐 겁니다. 벤더가 자기 제품의 한계를 먼저 입 밖에 냈다는 사실이 남습니다. 보안에서 제일 위험한 건 뚫리는 게 아니라 안 뚫린다고 믿는 쪽이니까요.

한 번 확인해 보세요. 지금 우리 서비스가 로드하는 모델은 정확히 어떤 리비전이고, 그게 마지막으로 바뀐 게 언제인가요. 바로 답이 안 나온다면 거기서부터 손대면 됩니다.

보안 AI 허깅페이스 제로트러스트 공급망보안

댓글

    댓글을 불러오는 중...