[실전 보안 07] 웹 방화벽이 걸러주는 것들

지난 편에서 요청 횟수 제한이 얼마나 자주 오느냐를 본다면, 웹 방화벽은 무엇이 담겨 오느냐를 본다고 했다. 이번 편은 그 웹 방화벽, 흔히 세 글자로 줄여 부르는 이 계층에 대한 이야기다. 내가 커뮤니티를 올린 클라우드 환경은 요청이 우리 서버 코드에 닿기 전에 앞단에서 훑어 주는 방화벽을 제공했다. 처음엔 이걸 켜 두기만 하면 다 알아서 막아 주는 만능 방패인 줄 알았는데, 직접 다뤄 보니 강점과 한계가 뚜렷했다. 그 실제 감각을 정리해 본다.


1. 웹 방화벽은 어디에 서 있나

웹 방화벽의 위치를 이해하는 게 먼저였다. 사용자의 요청은 우리 서버 코드에 도달하기 전에 여러 관문을 거친다. 웹 방화벽은 그 관문들 중 상당히 앞쪽, 그러니까 요청이 아직 우리 코드에 닿기 전에 선다. 여기서 요청의 주소, 헤더, 본문 같은 내용을 훑어 수상한 것을 걸러 낸다. 우리 코드가 위험한 요청을 아예 구경도 하지 않게 앞에서 막아 주는 셈이다.


이 앞단 위치가 중요한 이유가 있다. 방어가 우리 서버 안쪽에 있으면, 악성 요청이 이미 우리 자원을 소모한 뒤에야 걸린다. 반면 앞단에서 막으면 우리 서버는 그 요청 때문에 깨어날 필요조차 없다. 자원도 아끼고 위험도 줄인다. 첫 편에서 방어는 여러 겹이라고 했는데, 웹 방화벽은 그중 가장 바깥쪽에 가까운, 넓은 그물 역할을 하는 층이었다.


넓은 그물이라는 표현이 핵심이다. 웹 방화벽은 전 세계 수많은 사이트를 노리는 흔하고 일반적인 공격을 걸러 내는 데 강하다. 특정 사이트를 겨냥한 정교한 공격보다는, 인터넷 전체에 뿌려지는 자동화된 상투적 공격을 막는 게 주특기다. 첫 편에서 본, 존재하지도 않는 관리자 경로를 무작정 찔러 보던 그런 요청들이 바로 웹 방화벽이 잘 걸러 내는 대상이었다.


2. 어떤 공격을 걸러 내는가

웹 방화벽이 주로 막는 공격에는 잘 알려진 유형들이 있다. 대표적인 게 데이터베이스를 노리는 조작 시도다. 사용자 입력이 들어가는 자리에 데이터베이스 명령을 몰래 끼워 넣어, 서버가 그걸 실행하게 만들려는 공격이다. 이런 시도는 입력값에 특정한 형태의 조작 문자열이 담기는데, 웹 방화벽은 알려진 그 패턴을 인식해 요청을 차단한다.


또 하나는 사용자 화면에서 악성 스크립트를 실행시키려는 시도다. 입력란에 스크립트 조각을 넣어 다른 사용자의 브라우저에서 그게 실행되게 하려는 공격인데, 이 역시 전형적인 형태가 있어 앞단에서 상당수 걸러진다. 그 밖에 서버의 파일 경로를 넘겨보려는 시도, 알려진 취약점을 노려 흔한 경로를 두드리는 스캔도 웹 방화벽의 규칙에 걸린다. 이런 상투적 공격을 앞에서 쳐 내는 것만으로도 부담이 크게 줄었다.


이 대목에서 오해하지 말아야 할 게 있다. 웹 방화벽이 이런 공격을 걸러 준다고 해서, 내 코드가 그 공격에 안전해도 되는 건 아니라는 점이다. 웹 방화벽은 알려진 패턴을 막을 뿐, 조금만 변형해도 통과하는 요청이 있을 수 있다. 그래서 웹 방화벽은 보너스 방어층으로 두고, 근본 방어는 내 코드 안에서 입력을 제대로 다루는 데서 이뤄져야 했다. 데이터베이스 조작을 막는 진짜 방법은 뒤에서 다룰 안전한 질의 작성이지, 웹 방화벽에 기대는 게 아니었다.


3. 무료로 할 수 있는 것과 없는 것

신생 사이트를 1인으로 운영하는 입장에서 비용은 늘 현실적인 제약이었다. 다행히 내가 쓰는 환경은 기본적인 웹 방화벽 보호를 무료 범위에서 상당 부분 제공했다. 흔한 공격 패턴을 걸러 내는 기본 규칙, 악명 높은 출처를 차단하는 기능 같은 것들이 별도 비용 없이 켜져 있었다. 이것만으로도 앞서 말한 상투적 공격의 대부분이 막혔다.


하지만 무료 범위에는 한계도 뚜렷했다. 세밀한 사용자 정의 규칙, 예를 들어 특정 조건에서 요청 수를 숫자로 비교해 정교하게 제어하는 기능 같은 건 내 요금제에서 제약이 있었다. 그래서 앞 편에서 이야기했듯, 세밀한 요청 제어는 웹 방화벽에 맡기지 못하고 애플리케이션 코드 안에서 직접 구현하는 쪽으로 방향을 잡았다. 무엇을 앞단에 맡기고 무엇을 내 코드로 감당할지 선을 긋는 판단이 중요했다.


이 선 긋기에서 얻은 교훈은, 도구의 한계를 정확히 알아야 그 위에 올바른 보완을 쌓을 수 있다는 것이었다. 웹 방화벽이 다 해 줄 거라 막연히 믿었다면, 정작 그것이 못 막는 우리 서비스 고유의 남용에 무방비였을 것이다. 반대로 웹 방화벽이 이미 잘 막는 영역을 내 코드에서 또 붙들고 씨름했다면 시간 낭비였다. 각 층의 역할을 분명히 나누는 것이 효율적인 방어의 기본이었다.


4. 규칙은 양날의 검이다

웹 방화벽을 다루며 주의한 건 규칙이 너무 공격적이면 정상 요청까지 막는다는 점이었다. 방어 규칙은 수상한 패턴을 걸러 내는데, 때로는 멀쩡한 사용자의 정상적인 글이나 입력이 우연히 그 패턴과 비슷해 차단되는 일이 생긴다. 예를 들어 기술 이야기를 하다 보면 코드 조각이나 특수한 문자열을 글에 담는데, 이게 공격으로 오인될 수 있다. 우리 커뮤니티는 개발 이야기를 많이 다루니 이런 오탐이 특히 신경 쓰였다.


그래서 규칙을 세게 걸 때는 항상 오탐 가능성을 함께 저울질했다. 방어를 조이면 안전해지지만 정상 사용자의 정당한 활동이 막힐 위험이 커진다. 너무 풀면 편하지만 공격이 샌다. 이 균형점은 사이트의 성격에 따라 달랐고, 우리처럼 사용자가 자유롭게 글을 쓰는 커뮤니티는 지나치게 공격적인 내용 검사가 오히려 독이 될 수 있었다. 그래서 앞단에서는 명백한 공격만 거칠게 걸러 내고, 미묘한 판단은 우리 코드의 입력 정화에 맡기는 쪽을 택했다.


차단된 요청을 관찰하는 것도 운영의 중요한 부분이었다. 무엇이 얼마나 차단되는지 들여다보면 지금 어떤 공격이 유행하는지, 우리 사이트가 어떤 표적이 되고 있는지 감이 잡힌다. 방화벽은 단지 막는 벽이 아니라, 바깥의 위협을 관측하는 창이기도 했다. 이 관측을 통해 어떤 방어를 더 강화해야 할지 우선순위를 정할 수 있었다.


5. 바깥 방어에서 안쪽 방어로

여기까지 오면서 우리는 방어선의 바깥쪽을 훑었다. 봇 검증으로 사람을 가리고, 요청 제한으로 폭주를 세고, 웹 방화벽으로 흔한 악성 요청을 걸렀다. 이 세 층은 주로 우리 서버 바깥이나 입구에서 작동한다. 하지만 앞 편들에서 거듭 강조했듯, 이 방어들은 모두 우회될 수 있고, 특히 우리 서비스 고유의 위험은 앞단 도구가 알아서 막아 주지 않는다.


가장 대표적인 게 사용자가 직접 만들어 올리는 콘텐츠의 위험이다. 커뮤니티는 본질적으로 사용자가 글과 댓글을 쓰는 공간이다. 그런데 그 글 속에 악성 스크립트가 숨어 있어 다른 사용자의 브라우저에서 실행된다면, 이건 앞단 방화벽이 완벽히 막기 어려운 우리 내부의 문제다. 사용자 콘텐츠를 어떻게 안전하게 다룰지는 온전히 내 코드의 몫이었다.


웹 방화벽을 겪으며 얻은 가장 큰 태도 변화는, 앞단 도구를 신뢰하되 의존하지는 않는다는 것이었다. 방화벽이 있으니 안심이라고 여기는 순간 방심이 시작된다. 반대로 방화벽 따위 못 믿는다며 무시하면 공짜로 얻을 수 있는 넓은 방어를 버리는 셈이다. 있는 도구는 충분히 활용하되, 그것이 못 막는 영역을 정확히 알고 내 코드로 메운다. 이 균형 잡힌 태도가 한정된 자원으로 사이트를 지키는 1인 운영자에게는 가장 현실적인 전략이었다.


정리하면 앞단 방화벽은 흔한 공격을 값싸게 걸러 주는 고마운 첫 그물이지만, 우리 서비스 고유의 논리적 허점이나 사용자 콘텐츠의 위험까지 대신 책임져 주지는 않는다. 그 경계를 분명히 인식하는 것이 이 편의 결론이었다. 넓은 그물로 큰 것을 거르고, 촘촘한 손길로 미세한 것을 잡는 이중 구조, 그것이 바깥과 안쪽 방어를 나누는 이유였다.


그래서 다음 편부터는 방어의 안쪽, 우리 코드가 직접 책임져야 하는 영역으로 들어간다. 먼저 다룰 주제는 사용자 콘텐츠에 숨은 악성 스크립트, 즉 교차 사이트 스크립트 공격과 그것을 막는 입력 정화다. 커뮤니티 운영자에게 가장 현실적이고 자주 마주치는 위협인 만큼, 우리 사이트가 실제로 이 문제를 어떻게 다뤘는지 구체적으로 이야기하겠다. 방어의 무게 중심이 바깥에서 안으로 옮겨 가는 전환점이 될 것이다.