[실전 보안 01] 신생 사이트는 왜 표적이 되나

커뮤니티 사이트를 처음 열고 배포 버튼을 누른 그날, 나는 순진하게도 아무도 우리 사이트를 모를 테니 당분간은 조용할 거라고 생각했다. 그런데 접속 로그를 열어 본 순간 그 생각이 완전히 틀렸다는 걸 깨달았다. 방문자는 손에 꼽는데 로그인 시도와 회원가입 요청은 이미 수백 건씩 쌓이고 있었다. 사람이 아니라 자동화된 봇이 주소를 훑고 다니며 문을 두드리고 있었던 것이다. 이 시리즈는 그 첫날의 충격에서 시작해, 내가 작은 커뮤니티를 지키기 위해 실제로 세워 나간 방어선들을 순서대로 정리한 기록이다.


1. 아무도 모르는 사이트에 왜 봇이 오는가

가장 먼저 든 의문은 이거였다. 홍보도 안 했고 검색에도 거의 안 걸리는 신생 사이트에 대체 누가 이렇게 찾아온단 말인가. 답은 간단했다. 그들은 우리 사이트를 골라서 온 게 아니라, 인터넷 전체를 기계적으로 훑다가 응답하는 서버를 발견하면 무조건 찔러 보는 자동 스캐너였다. 공개된 아이피 대역과 도메인 목록을 통째로 순회하면서 열려 있는 포트와 살아 있는 웹 서버를 수집하는 프로그램은 지금 이 순간에도 쉬지 않고 돌아간다.


이들에게 우리 사이트가 얼마나 유명한지는 중요하지 않다. 응답을 준다는 사실 자체가 표적이 될 이유다. 오히려 신생 사이트는 더 매력적인 먹잇감이다. 운영자가 보안에 익숙하지 않을 가능성이 높고, 기본 설정을 그대로 쓰거나 흔한 관리자 경로를 열어 둔 경우가 많기 때문이다. 봇 입장에서는 방어가 허술한 새 서버가 훨씬 뚫기 쉬운 대상인 셈이다.


실제로 초기 로그를 분석해 보니 요청의 상당수가 존재하지도 않는 관리 페이지 주소나 흔한 설정 파일 경로를 무작위로 찔러 보는 것이었다. 특정 게시판 프로그램에서만 쓰는 경로, 널리 알려진 프레임워크의 기본 관리자 주소가 순서대로 들어왔다. 우리 사이트가 그런 프로그램을 쓰지 않는데도 말이다. 이건 사람이 고민해서 보낸 요청이 아니라, 전 세계 서버에 똑같이 뿌리는 자동 공격 시나리오의 일부였다.


이 사실을 이해하고 나니 마음가짐이 달라졌다. 봇의 방문은 내가 뭘 잘못해서 생긴 특별한 일이 아니라, 인터넷에 서버를 올린 이상 누구나 겪는 기본 배경 소음이었다. 그러니 봇이 온다는 사실에 놀랄 게 아니라, 봇이 올 것을 당연한 전제로 깔고 처음부터 설계에 반영해야 했다. 방어를 나중에 덧붙이는 기능이 아니라 사이트의 기본 골격으로 삼는 태도, 그게 이 시리즈 전체를 관통하는 첫 번째 교훈이었다.


2. 봇이 노리는 것들의 목록

방어를 설계하려면 먼저 상대가 무엇을 원하는지 알아야 했다. 관찰해 보니 자동화된 남용은 크게 몇 가지 목적으로 나뉘었다. 첫째는 계정 탈취였다. 다른 사이트에서 유출된 아이디와 비밀번호 목록을 그대로 우리 로그인 창에 대입해 보는 방식이다. 사람들이 여러 사이트에 같은 비밀번호를 돌려쓰는 습관을 노린 공격으로, 흔히 크리덴셜 스터핑이라고 부른다.


둘째는 스팸과 광고 도배였다. 회원가입을 자동으로 반복해 계정을 대량으로 만든 뒤, 그 계정으로 도박이나 불법 상품 링크가 담긴 글과 댓글을 쏟아붓는다. 신생 커뮤니티는 글이 적어 도배가 눈에 잘 띄고, 검색 노출을 노린 링크 심기의 숙주로도 자주 이용된다. 셋째는 자원 소진이었다. 짧은 시간에 같은 요청을 폭주시켜 서버 비용을 끌어올리거나 아예 응답 불능 상태로 만드는 시도다.


넷째는 데이터 긁어 가기였다. 우리가 공들여 쌓은 글과 사용자 정보를 통째로 복사해 다른 곳에 재활용하려는 자동 수집이다. 넷 다 성격은 다르지만 공통점이 있었다. 사람 한 명이 손으로 할 수 있는 규모가 아니라, 기계가 지치지 않고 반복한다는 점이다. 그래서 방어의 핵심도 결국 사람과 기계를 구분하고, 기계의 반복을 값비싸게 만드는 데 있다는 걸 이때 처음 감을 잡았다.


3. 작은 사이트일수록 자동화된 방어가 필요하다

대기업은 보안 전담 인력이 밤낮으로 로그를 지켜본다. 하지만 나 같은 1인 개발자는 잠도 자야 하고 다른 일도 해야 한다. 사람이 실시간으로 감시하는 방식은 애초에 불가능하다는 뜻이다. 그래서 나는 처음부터 방향을 정했다. 사람이 개입하지 않아도 스스로 작동하는 자동 방어 장치를 여러 겹으로 쌓자는 것이었다.


이 관점이 왜 중요하냐면, 방어를 한 번 잘 설정해 두면 그 뒤로는 봇이 아무리 몰려와도 추가 노동이 들지 않기 때문이다. 반대로 임시로 막는 방식, 예를 들어 악성 아이피를 발견할 때마다 손으로 차단 목록에 넣는 식은 금방 한계에 부딪힌다. 봇은 아이피를 수시로 바꾸고, 나는 그 속도를 따라갈 수 없다. 근본적으로 사람과 기계를 자동으로 구분하는 장치가 있어야 지속 가능하다.


이 결정 덕분에 나중에 크게 덕을 봤다. 초기에 자동 방어를 촘촘히 깔아 둔 로그인 창은, 몇 달 뒤 유출 목록을 대입하는 대규모 시도가 들어왔을 때도 별다른 손을 대지 않고 버텨 냈다. 만약 그때 손으로 아이피를 막고 있었다면 나는 잠도 못 자고 화면만 들여다보고 있었을 것이다. 자동화된 방어에 초기 투자를 아끼지 않은 것이 결국 가장 값싼 선택이었다는 걸 시간이 지나서야 실감했다.


내가 커뮤니티를 올린 클라우드 환경은 다행히 이런 자동 방어에 필요한 부품들을 기본으로 제공했다. 요청이 우리 서버 코드에 닿기도 전에 걸러 주는 앞단의 방화벽, 사람 여부를 조용히 확인하는 위젯, 짧은 시간의 요청 횟수를 세는 장치 같은 것들이다. 이 부품들을 어떻게 조합해 여러 겹의 방어선으로 엮었는지가 앞으로 이 시리즈에서 다룰 이야기의 뼈대다.


4. 방어는 한 겹이 아니라 여러 겹이다

보안을 공부하면서 가장 크게 바뀐 생각은 완벽한 단 하나의 방어벽이라는 환상을 버린 것이었다. 어떤 방어든 언젠가는 우회되거나 뚫린다는 전제를 깔고 시작해야 한다. 그래서 등장하는 개념이 심층 방어다. 문을 하나만 잠그는 게 아니라, 대문, 현관, 방문, 금고까지 층층이 잠가서 하나가 뚫려도 다음 층이 버티게 만드는 발상이다.


예를 들어 로그인 하나만 봐도 방어는 여러 겹이다. 앞단 방화벽이 악명 높은 아이피를 먼저 걸러 내고, 그다음 자동 요청 폭주를 횟수 제한으로 막고, 그래도 통과한 요청은 사람 확인 위젯으로 걸러 내고, 마지막으로 비밀번호가 유출되더라도 안전하게 저장하는 해싱이 버틴다. 어느 한 겹만 믿었다면 그 겹이 뚫리는 순간 끝이지만, 여러 겹이 있으면 공격자는 모든 층을 다 넘어야 한다.


이렇게 층을 나누면 방어를 설계하고 점검하기도 훨씬 쉬워진다. 각 층이 맡은 역할이 분명하니, 어디가 약한지 진단할 때도 층별로 따져 보면 된다. 이 시리즈의 구성 자체도 그 층을 따라간다. 인증과 비밀번호 같은 안쪽 층부터 시작해, 봇 방어와 요청 제한 같은 바깥 층, 그리고 콘텐츠 보안과 시크릿 관리 같은 운영 층까지 차례로 짚어 나갈 계획이다.


5. 겁먹기보다 구조를 이해하기

이런 이야기를 들으면 보안이 너무 어렵고 무섭게 느껴질 수 있다. 나도 처음엔 로그를 보고 덜컥 겁이 났다. 하지만 하나씩 뜯어 보니 결국 몇 가지 원리의 반복이었다. 사람과 기계를 구분한다, 반복을 값비싸게 만든다, 민감한 정보는 절대 평문으로 두지 않는다, 신뢰를 함부로 주지 않고 매번 확인한다. 이 네 가지 원리만 몸에 익히면 대부분의 결정이 자연스럽게 따라왔다.


중요한 건 완벽을 목표로 하지 않는 것이었다. 세상에 뚫리지 않는 사이트는 없다. 다만 우리 사이트를 뚫는 데 드는 비용을 충분히 높여 두면, 자동 공격자는 더 쉬운 다음 먹잇감으로 옮겨 간다. 자물쇠가 도둑을 완전히 막지는 못해도 옆집보다 튼튼하면 우리 집은 넘어간다는 것과 같은 이치다. 신생 사이트에게 현실적인 목표는 무적이 아니라 성가심이다.


다음 편부터는 이 방어선을 가장 안쪽, 그러니까 사용자가 누구인지 확인하는 인증에서부터 풀어 나가려 한다. 로그인이 어떻게 사용자를 기억하는지, 세션과 토큰은 무엇이 다른지, 우리 사이트는 왜 토큰 방식을 골랐는지를 실제 경험을 곁들여 이야기하겠다. 인증은 모든 방어의 출발점이자, 잘못 설계하면 나머지 층이 아무리 튼튼해도 무너지는 가장 중요한 첫 단추다. 그럼 다음 편에서 이어 가겠다.