> THE-ARSENAL
> _

시스템의 붕괴는 거대하고 요란한 망치질에서만 비롯되지 않는다. 때로 가장 치명적인 파국은, 우리가 완벽하게 신뢰했던 두 개의 프로세스가 서로를 바라보는 방식이 아주 미세하게 어긋나는 그 찰나의 순간에서 시작된다. 우리는 시스템을 설계할 때 수많은 관문과 검증 절차를 둔다. 허가된 사용자인가? 올바른 요청인가? 범위에 맞는가? 수많은 확인 절차를 통과한 요청은 비로소 안전하다고 신뢰받으며 핵심부로 전달된다. 문제는 바로 이 신뢰가 시작되는 지점에 있다. 우리는 첫 번째 관문의 검사가 완벽했다고 가정한 나머지, 두 번째 실행자는 그저 맹목적으로 명령을 수행할 것이라 믿어버린다. 하지만 만약, 검사하는 주체와 실행하는 주체가 동일한 데이터를 조금이라도 다르게 해석한다면 어떻게 될까. 이 미세한 불일치, 검사 시점과 실행 시점 사이의 이 짧은 시간적 간극이 바로 거대한 성벽을 무너뜨리는 가장 날카로운 균열이다.

검사 시점과 실행 시점의 불일치란 무엇인가

보안의 세계에는 이 위험을 지칭하는 고전적인 용어가 있다. Time-of-check to time-of-use. 줄여서 TOCTOU. 이름 그대로 무언가를 확인한 시점과 그것을 실제로 사용한 시점 사이에 발생하는 시간차를 이용한 공격 기법을 의미한다. 이는 시스템이 어떤 자원이나 정보의 상태를 확인한 후, 그 확인된 정보를 기반으로 행동을 취하기 직전, 그 짧은 틈을 비집고 들어와 자원이나 정보의 상태를 악의적으로 변경하는 방식이다. 이 공격이 성공하는 이유는 단 하나, 시스템이 한 번 검사한 것은 영원히 유효할 것이라고 순진하게 믿어버리기 때문이다. 검사와 실행은 논리적으로는 붙어있는 것처럼 보이지만, 물리적으로는 아주 미세한 시간 간격을 포함한다. 해커에게는 그 나노초의 시간만으로도 충분하다. 그들은 빛의 속도로 이 틈새에 끼어들어, 시스템의 신뢰 기반 자체를 무너뜨린다.

파일 시스템, 검증 후에 바뀌는 목표물

이 고전적인 수법이 가장 흔하게 사용된 무대는 파일 시스템이다. 아주 오래전부터 존재했던 이 방식은 지금도 여전히 유효하다. 시나리오는 이렇다. 어떤 프로그램이 특정 파일을 실행하기 전에, 먼저 그 파일이 안전한지, 혹은 현재 사용자가 그 파일을 실행할 권한이 있는지 확인한다. 예를 들어, 시스템은 사용자가 '안전한 A파일'을 실행하려 한다고 인지하고, A파일의 권한을 꼼꼼하게 검사한다. 모든 검사를 통과한 후, 시스템은 이제 A파일을 실행해도 좋다고 결론 내린다. 그리고 운영체제에 명령한다. A파일을 실행하라. 바로 이 순간이다. 권한 검사가 끝난 직후, 그리고 실제 실행 명령이 떨어지기 직전. 공격자는 눈 깜짝할 사이에 원래의 '안전한 A파일'을 치워버리고, 그 자리에 '악성 B파일'을 대신 가져다 놓는다. 이때 사용되는 기술이 심볼릭 링크와 같은 기법이다. 겉모습은 A파일이지만, 실제로는 B파일을 가리키도록 연결고리를 바꿔치기하는 것이다. 시스템은 자신이 검사했던 파일이 이미 다른 것으로 바뀌었다는 사실을 알지 못한 채, 자신이 부여한 신뢰를 바탕으로 악성 B파일을 실행하고 만다. 검사 주체와 실행 주체가 동일한 파일 경로를 신뢰했지만, 그 경로가 가리키는 실제 대상이 중간에 바뀌어버린 것이다.

온라인 상거래, 결제 직전에 조작되는 가격표

이 개념은 물리적인 파일을 넘어 디지털 데이터 영역에서도 동일하게 작동한다. 온라인 쇼핑몰의 장바구니를 생각해보자. 사용자가 10만 원짜리 물건을 장바구니에 담고 결제 페이지로 이동한다. 웹 애플리케이션은 첫 번째 단계로 장바구니의 총액을 '검사'한다. 총액은 10만 원. 유효한 금액이다. 시스템은 이 정보를 바탕으로 다음 결제 모듈을 호출할 준비를 한다. 이 과정은 보통 여러 개의 네트워크 요청과 데이터베이스 호출로 이루어진다. 이 복잡한 절차 속에서 반드시 미세한 시간차가 발생한다. 공격자는 이 틈을 놓치지 않는다. 결제 '실행' 버튼을 누르기 직전, 혹은 시스템이 결제 모듈을 호출하는 그 찰나에, 공격자는 두 번째 요청을 날려 장바구니의 가격 데이터를 1만 원으로 조작해버린다. 결제 모듈은 이미 앞선 단계에서 총액 검사가 끝났다고 신뢰하고 있기 때문에, 조작된 1만 원이라는 데이터를 그대로 받아들여 결제를 '실행'한다. 10만 원짜리 물건이 1만 원에 결제되는 순간이다. 이 역시 검사 시점의 데이터와 실행 시점의 데이터가 불일치하여 발생한 문제다. 시스템은 분리된 두 개의 동작을 하나의 논리적 흐름으로 묶었지만, 그 사이의 시간적 간극을 보호하지 못했다.

데이터 해석의 불일치, 가장 은밀한 균열

이제 가장 현대적이고 은밀한 형태의 불일치를 이야기해 보자. 이것은 파일이나 데이터가 바뀌는 것이 아니라, 애초에 두 개의 다른 시스템이 동일한 데이터를 완전히 다르게 해석하는 문제다. 복잡한 모듈형 시스템을 상상해 보자. 이 시스템에는 두 명의 관리자가 있다. 첫 번째 관리자는 정문에서 방문객의 신분증과 방문 목적지를 검사하는 '관문 경비원'이다. 두 번째 관리자는 실제 보물이 있는 지하 금고에서 방문객이 요청한 물건을 꺼내주는 '금고 관리자'다. 어느 날, 한 방문객이 정문을 찾아온다. 그의 방문 목적지 서류에는 '지하 1층, 공용 테스트 구역'이라고 적혀 있다. 경비원은 이 서류를 보고 안심한다. 매우 안전하고 중요하지 않은 구역이기 때문이다. 그런데 사실 이 서류에는 아주 작은 글씨로 특수 주석이 달려 있었다. '단, 해당 구역 도착 시, 두 칸 뒤로 물러나 옆방으로 진입할 것.' 경비원은 이 주석의 의미를 알지 못한다. 그는 오직 '방문 목적지가 공용 테스트 구역으로 시작하는가'라는 규칙만 배웠기 때문에, 이 방문객을 통과시킨다. 방문객은 정문을 통과해 지하의 금고 관리자에게 간다. 금고 관리자는 정문을 통과했다는 확인 도장을 신뢰하고, 방문객의 서류를 넘겨받는다. 그런데 금고 관리자는 경비원과는 달리, 그 작은 주석을 '중요한 실행 명령어'로 해석하도록 훈련받았다. 그는 서류에 적힌 대로, 방문객을 테스트 구역으로 안내하는 대신, 두 칸 뒤로 물러나 옆방, 즉 '지하 1층, 개인 자산 금고'의 문을 열어준다. 시스템은 붕괴된다. 검사 시점(경비원)과 실행 시점(금고 관리자)이 동일한 서류를 받았지만, 서류에 포함된 '특수 명령어'를 해석하는 방식이 달랐기 때문에 발생한 참사다.

방어 전략, 검사와 실행을 하나로 묶기

그렇다면 이 치명적인 시간차 공격을 어떻게 막을 수 있을까. 전략은 두 가지로 나뉜다. 첫 번째는 검사와 실행을 논리적으로뿐만 아니라 물리적으로도 하나의 동작으로 묶어버리는 것이다. 이를 '원자적 실행'이라 부른다. 앞서 말한 파일 시스템의 예에서, 파일의 권한을 '검사하는 동작'과 파일을 '실행하는 동작'을 분리하지 않고, 운영체제 수준에서 '권한을 확인하고 즉시 잠근 뒤 실행하는' 단일 명령어를 사용하는 것이다. 이 두 동작 사이에는 그 어떤 다른 프로세스도 끼어들 수 없게 된다. 두 번째 전략은 더 단순하고 확실하다. 바로 신뢰를 버리는 것이다. 첫 번째 관문(경비원)이 아무리 꼼꼼히 검사했더라도, 두 번째 실행자(금고 관리자)는 그 검사 결과를 절대 믿지 않고, 금고 문을 열기 직전에 자신의 기준대로 처음부터 다시 검사하는 것이다. 금고 관리자가 방문객의 서류를 받고, 서류에 적힌 '특수 명령어'가 있는지 확인한 뒤, 이 명령어가 자신의 규칙에 위배된다면 즉시 요청을 거부하는 것이다. 이 방식은 실행 직전에 다시 한번 유효성을 검증함으로써, 검사 시점과 실행 시점의 간극을 원천적으로 제거한다.

내부의 신뢰를 검증하라

우리는 종종 외부의 위협에 대비해 성벽을 높이 쌓는 데 집중한다. 하지만 정작 가장 치명적인 붕괴는 이미 성벽을 통과한, 우리가 신뢰하기로 결정한 내부 컴포넌트들 사이의 미세한 오해에서 비롯된다. 하나의 시스템, 하나의 모듈이 데이터를 검증했다는 사실이, 다른 모듈이 그 데이터를 맹목적으로 신뢰해도 된다는 의미는 결코 아니다. 두 개의 다른 컴포넌트가 동일한 데이터를 신뢰하고 각자의 역할을 수행해야 한다면, 우리는 반드시 질문해야 한다. 그들은 과연 이 데이터를 완벽하게 동일한 방식으로 해석하고 있는가? 이 해석의 불일치 속에 시스템 전체를 무너뜨릴 수 있는 치명적인 균열이 숨어있을지 모른다.

3줄 요약

  1. 시스템의 치명적 결함은 종종 무언가를 '검사'한 시점과 '실행'하는 시점 사이의 시간차에서 발생한다.
  2. 공격자는 이 미세한 틈을 이용해, 검증이 끝난 데이터나 파일을 악의적인 것으로 바꿔치기하여 시스템을 속인다.
  3. 가장 은밀한 공격은 두 개의 내부 모듈이 동일한 데이터를(예: 특수 명령어) 서로 다르게 해석할 때 발생하며, 이는 권한 상승 등의 악성 행위로 이어질 수 있다.