> THE-ARSENAL
> _

우리는 흔히 해킹이라고 하면 어두운 방 구석에서 검은 후드 티를 뒤집어쓴 천재가 녹색 글자가 비 오듯 쏟아지는 모니터를 보며 미친 듯이 키보드를 두드리는 장면을 상상한다. 영화가 만들어낸 아주 게으른 클리셰다. 현실의 진짜 고수들은 그렇게 요란하게 일하지 않는다. 그들은 땀 흘리며 굳게 닫힌 창문을 깨부수거나, 최첨단 레이저 보안 시스템을 곡예 하듯 피하지 않는다. 대신 그들은 아주 말끔한 정장을 입고 현관으로 걸어가 초인종을 누른다. 그리고 아주 정중하게 묻는다. "택배 왔습니다. 문 좀 열어주시겠어요?"

이때 집주인이 의심 없이 문을 열어준다면, 보안 시스템이 아무리 비싸고 견고한들 무슨 소용이 있겠는가. 이것이 바로 오늘 우리가 이야기할 주제의 핵심이다. 시스템은 완벽하다. 암호화 알고리즘은 수학적으로 깨지지 않았고, 방화벽은 모든 비정상적인 접근을 차단하고 있다. 하지만 해커는 유유히 그 시스템의 가장 깊은 곳을 활보한다. 그는 코드를 부순 것이 아니다. 시스템을 설계한 사람의 '믿음'을 배신했을 뿐이다. 이것이 바로 기술적인 버그보다 훨씬 더 치명적이고 찾아내기 힘든, '로직 해킹(Logic Hacking)'의 세계다. 코드는 거짓말을 하지 않지만, 코드를 짠 사람은 거짓말 같은 착각에 빠지기 때문이다.

개발자의 착각: "설마 이렇게까지 하겠어?"의 대가

소프트웨어를 만드는 사람들은 기본적으로 성선설을 믿는 경향이 있다. 아니, 성선설이라기보다는 '합리적 인간설'을 믿는다. 그들이 상상하는 사용자는 아주 모범적이고 상식적인 사람들이다. 쇼핑몰에 들어오면 물건을 고르고, 장바구니에 담고, 결제 버튼을 누르고, 돈을 내고 나가는 사람들 말이다. 개발자는 이 합리적인 흐름, 즉 '프로세스'를 코드로 구현하는 데 밤을 지새운다. 그리고 생각한다. "사용자는 내가 만든 길을 따라 걷겠지."

바로 이 지점이 해커가 파고드는 틈새다. 해커의 통찰은 "왜 사용자가 꼭 그렇게 행동해야 해?"라는 삐딱한 질문에서 시작된다. 개발자는 사용자가 나이 입력 칸에 당연히 양수를 넣을 것이라 믿는다. 그래서 숫자인지 아닌지만 검사하는 코드를 넣는다. 하지만 해커는 그 칸에 '-5'를 입력해 본다. 개발자는 "설마 나이를 마이너스로 적는 미친 사람이 있겠어?"라고 생각하며 그에 대한 방어 코드를 '생략'한다. 바로 그 '생략'이 해커에게는 초대장이다.

만약 시스템이 마이너스 나이를 받아들여서 "당신은 태어날 때까지 5년 남았군요"라고 계산해 버린다면, 이건 단순한 웃음거리가 아니다. 만약 이것이 쇼핑몰 결제 수량이었다면 어떨까? 수량에 '-1'을 입력하고 결제를 눌렀을 때, 시스템이 멍청하게 "가격 x 수량" 공식을 그대로 적용해 "-10,000원"을 청구한다면? 그리고 그 결과 내 계좌에서 돈이 나가는 게 아니라, 오히려 쇼핑몰이 나에게 10,000원을 환불해 주는 로직이 작동한다면?

이것은 코드상의 오류가 아니다. 곱셈 함수는 정확하게 작동했다. 문제는 '수량은 음수가 될 수 없다'는, 너무나 당연해서 굳이 코드로 적어놓지도 않았던 개발자의 '암묵적인 믿음'이 깨진 것이다. 해커는 이처럼 개발자가 "설마 이렇게까지 하겠어?"라고 방심하며 남겨둔 여백을 집요하게 찾아내어 자신의 놀이터로 만든다. 그들은 개발자가 상상조차 하기 싫어했던 비합리적인 행위들을 끈질기게 시도하며, 시스템이 당황해서 뱉어내는 예상 밖의 결과물들을 챙겨간다.

해피 패스(Happy Path)의 함정: 꽃길만 걷는 테스트의 최후

개발 용어 중에 '해피 패스(Happy Path)'라는 말이 있다. 아무런 예외 상황 없이, 에러도 없이, 사용자가 의도한 대로 아주 매끄럽게 목표까지 도달하는 경로를 뜻한다. 대다수의 테스트 코드는 이 해피 패스가 잘 작동하는지 확인하는 데 집중된다. 로그인하고, 상품을 검색하고, 결제하는 그 아름다운 꽃길 말이다. QA(품질 보증) 팀도 주로 이 길이 뚫려 있는지를 확인한다. 길이 매끄럽게 닦여 있으면 그들은 "출시 준비 완료" 도장을 찍는다.

하지만 해커는 꽃길을 걷지 않는다. 그들은 덤불 숲을 헤치고, 울타리를 넘고, 때로는 하수구를 기어 다닌다. 이를 '다크 패스(Dark Path)'라고 불러도 좋을 것이다. 해피 패스만 검증된 시스템은, 정해진 길을 벗어나는 순간 무방비 상태가 된다. 마치 기차 레일 위만 달릴 줄 아는 기차가 탈선했을 때 아무런 조향 능력이 없는 것과 같다.

예를 들어보자. 은행 앱에서 송금하는 과정을 생각해 보자. 1단계: 계좌 비밀번호 확인, 2단계: 잔액 확인, 3단계: 송금 실행. 개발자는 사용자가 반드시 1, 2, 3단계를 거칠 것이라 믿고 코드를 짠다. 그런데 만약 해커가 특수한 도구를 써서 1단계와 2단계를 건너뛰고 바로 3단계 기능을 호출해 버리면 어떻게 될까?

놀랍게도 많은 시스템이 여기서 무너진다. 3단계 코드는 당연히 앞선 단계들이 완료되었을 거라 '믿고' 작동하기 때문이다. "여기까지 왔으면 비밀번호랑 잔액은 다 확인된 거겠지?"라는 안일한 믿음. 그 믿음 때문에 3단계 코드는 다시 한번 잔액을 검사하지 않고 송금을 처리해 버린다. 해피 패스 바깥의 세상은 이토록 냉혹하다. 꽃길만 걷던 시스템은 진흙탕에 발을 들이는 순간, 자신이 얼마나 나약한 존재인지 깨닫게 되지만 이미 금고는 털린 뒤다.

인과관계 비틀기: 결제하고 물건 받기 vs 물건 받고 환불만 하기

논리적 해킹의 정수는 인과관계를 비트는 데 있다. 현실 세계에서는 원인 없는 결과가 있을 수 없다. 물건을 사야 영수증이 나오고, 밥을 먹어야 배가 부르다. 하지만 디지털 세계, 특히 웹과 앱의 세계에서 이 인과관계는 꽤나 헐거운 실로 연결되어 있다. 그리고 해커는 그 실을 끊어서 다시 묶는 재주가 있다.

가장 흔한 예가 웹페이지의 '순서'를 조작하는 것이다. 보통의 웹사이트는 페이지 A에서 버튼을 눌러야 페이지 B로 넘어가도록 설계되어 있다. 하지만 이것은 눈에 보이는 화면(UI)상의 이야기일 뿐이다. 실제 서버와의 통신(API) 레벨에서 보면, 페이지 A와 페이지 B는 독립적인 요청이다. 해커는 페이지 A를 거치지 않고 페이지 B의 주소(URL)를 직접 입력하거나, API를 직접 호출해 버린다.

이것을 좀 더 악랄하게 응용하면 '레이스 컨디션(Race Condition, 경쟁 상태)'이라는 고급 기술이 된다. 예를 들어, 쿠폰을 사용하면 포인트가 차감되는 로직이 있다고 치자. 정상적인 인과관계는 [쿠폰 사용 요청 -> 유효성 검사 -> 사용 처리 -> 포인트 차감]이다. 이 과정은 눈 깜짝할 새에 일어나지만, 컴퓨터의 시간으로 보면 분명한 순서가 있다.

해커는 여기서 '동시성'이라는 무기를 꺼낸다. 쿠폰 사용 요청을 0.001초 간격으로 100개를 동시에 보내버리는 것이다. 서버가 첫 번째 요청을 받고 "어? 유효한 쿠폰이네?"라고 판단하고 사용 처리를 하려는 찰나, 두 번째 요청이 들어온다. 아직 첫 번째 요청에 대한 '포인트 차감'이 이루어지지 않았기 때문에, 서버는 두 번째 요청도 "어? 아직 포인트가 있네? 유효한 쿠폰이네?"라고 판단해 버린다.

결국 인과관계의 틈새, 즉 '검사'와 '실행' 사이의 아주 미세한 시간차를 파고들어, 쿠폰은 한 장인데 물건은 100개를 사는 기적을 행한다. 물리 법칙이 지배하는 현실에서는 불가능한 '인과율의 붕괴'가, 개발자의 헐거운 믿음으로 지탱되는 코드 속에서는 빈번하게 일어난다.

100원으로 100만 원짜리 티켓을 산 남자의 논리

이쯤에서 아주 고전적이면서도, 로직 해킹의 본질을 가장 잘 보여주는 실제 사례를 하나 살펴보자. 과거 한 해외 항공사 예약 시스템에서 벌어진 일이다. 한 남자가 1등석 항공권을 단돈 몇백 원에 구매했다. 그는 천재적인 프로그래머도 아니었고, 항공사 서버를 해킹해서 데이터베이스를 조작한 것도 아니었다. 그는 그저 웹 브라우저의 기본 기능을 조금 영리하게 사용했을 뿐이다.

우리가 인터넷 쇼핑을 할 때 화면에 보이는 '가격' 정보는 어디서 올까? 당연히 서버에서 계산해서 보내준다. "이 비행기표는 100만 원입니다"라고 HTML 코드에 적어서 보내주는 것이다. 그리고 우리가 '결제' 버튼을 누르면, 브라우저는 이 HTML에 적혀 있던 '100만 원'이라는 정보를 다시 서버로 보낸다. "사용자가 100만 원짜리 결제를 요청했습니다"라고 말이다.

여기서 거대한 '신뢰의 배신'이 발생한다. 개발자는 이렇게 믿었다. "내가 처음에 100만 원이라고 보내줬으니, 다시 돌아오는 값도 당연히 100만 원이겠지." 하지만 사용자의 컴퓨터(클라이언트)는 사용자의 통제하에 있는 공간이다. 그 남자는 '결제' 버튼을 누르기 직전, 웹 브라우저의 개발자 도구를 켰다. 그리고 화면에 보이는 가격 정보를 '100'으로 수정했다. 그러고 나서 결제 버튼을 눌렀다.

놀랍게도 결제는 승인되었다. 서버는 브라우저가 보내준 '100원'이라는 정보를 의심 없이 믿었다. 왜냐하면 그 정보는 원래 자기가 보냈던 것이니까. 자기가 뱉은 말을 자기가 다시 주워담으면서, 그 사이에 그 말이 변질되었을 것이라고는 상상도 하지 못한 것이다. 이것을 '파라미터 변조(Parameter Tampering)'라고 한다.

이 사건은 전 세계 보안 업계에 큰 충격을 주었다. 아무리 복잡한 암호화를 적용하고, 수십억짜리 방화벽을 세워도, "클라이언트에서 오는 데이터는 절대 믿지 말라"는 기본 원칙 하나를 어기면 모든 것이 무용지물이 된다는 것을 적나라하게 보여주었기 때문이다. 문은 강철로 만들었지만, 문을 여는 열쇠가 진짜인지 확인하는 경비원이 졸고 있었던 셈이다.

모든 것을 의심하라: 제로 트러스트(Zero Trust)가 일상이 된 세상

결국 이 모든 이야기의 끝은 하나의 결론으로 귀결된다. "아무것도 믿지 마라." 너무 삭막하게 들리는가? 하지만 디지털 세상에서 이것은 생존을 위한 유일한 철학이다. 보안 업계에서는 이것을 '제로 트러스트(Zero Trust)' 모델이라고 부른다.

과거의 보안은 '경계'를 지키는 것이었다. 성벽을 높게 쌓고, 성문을 굳게 잠그면 성 안은 안전할 것이라고 믿었다. 한번 로그인에 성공한 사용자, 내부망에 접속한 직원은 '믿을 수 있는 사람'으로 간주했다. 하지만 로직 해킹은 그 믿음이 얼마나 부질없는 것인지 증명했다. 내부 직원의 ID도 도용될 수 있고, 로그인한 사용자가 악의적인 요청을 보낼 수도 있으며, 100만 원짜리 티켓이 0.1초 만에 100원이 될 수도 있다.

이제 우리는 성벽을 없애고, 모든 방문을 하나하나 검문하는 세상으로 가고 있다. 사용자가 버튼을 누를 때마다, 페이지를 이동할 때마다, 데이터를 보낼 때마다 시스템은 의심하고 또 의심해야 한다. "너 아까 로그인한 그 사람 맞아?", "이 가격표, 내가 준 거 그대로 가져온 거 맞아?", "너 왜 1단계 건너뛰고 3단계로 바로 왔어?"

개발자들에게는 고달픈 일이다. 모든 코드 줄마다 의심을 심어야 하고, 성능 저하를 감수하면서까지 중복 검사를 수행해야 한다. 하지만 어쩌겠는가. 우리가 만든 시스템은 너무나 복잡해졌고, 그 틈새를 노리는 눈들은 점점 더 날카로워지고 있다.

우리 모두에게도 마찬가지다. 눈에 보이는 화면, "안전하다"는 문구, 자물쇠 아이콘을 너무 맹신하지 말자. 디지털 세상의 문은 잠겨 있어도, 그 문을 여는 열쇠는 언제든 복제될 수 있고, 심지어 문 자체가 가짜일 수도 있다. 의심하라. 그리고 검증하라. 그것만이 이 보이지 않는 믿음의 전쟁터에서 당신의 자산을 지키는 유일한 열쇠가 될 것이다.

3줄 요약

  1. 해킹은 코드를 부수는 것이 아니라, 개발자의 '설마' 하는 안일한 믿음과 논리적 허점을 찌르는 심리전이다.
  2. '해피 패스'만 검증된 시스템은 인과관계를 비틀거나 데이터를 변조하는 비정상적인 시도(다크 패스)에 속수무책으로 무너진다.
  3. "클라이언트(사용자)가 보낸 데이터는 절대 믿지 말라"는 제로 트러스트 원칙만이 유일한 방어책이다.