![[실전 커뮤니티 09] 스팸과 어뷰징 방어](https://img.thenullpage.com/posts/5730/5730_1_aa3986.webp)
지난 편의 신고와 모더레이션이 사람의 잘못을 다뤘다면, 이번 편은 기계적인 위협을 다룬다. 자동화된 스팸, 무한 도배, 대량 계정 생성 같은 것들이다. 이런 위협은 사람의 판단으로 일일이 막을 수 없고, 시스템의 방벽으로 걸러야 한다. 내가 커뮤니티를 지키기 위해 세운 방어선들에 대한 기록이다.
1. 스팸은 왜 반드시 찾아오는가
커뮤니티가 조금이라도 알려지면 스팸은 반드시 찾아온다. 이건 우리 커뮤니티가 매력적이어서가 아니라, 스팸이 자동화됐기 때문이다. 스패머는 사람을 골라 공격하지 않는다. 인터넷을 훑는 기계가 글을 쓸 수 있는 빈칸을 발견하면 자동으로 광고를 쏟아붓는다. 우리 커뮤니티는 그 무수한 빈칸 중 하나일 뿐이다. 그래서 규모와 상관없이 방어는 필수였다.
스팸의 목적은 대개 링크였다. 도박, 불법 광고, 낚시성 사이트로 유도하는 링크를 최대한 많은 곳에 뿌리는 것이다. 이들에게 우리 커뮤니티는 자기 링크를 노출할 공짜 게시판이었다. 방치하면 목록이 순식간에 광고로 뒤덮이고, 진짜 사용자는 쓰레기장이 된 공간을 보고 떠난다. 첫 편에서 말한 첫인상이, 스팸 하나로 무너질 수 있었다.
더 무서운 건 스팸이 신뢰를 좀먹는다는 점이다. 사용자는 커뮤니티가 스팸을 방치하면, 이곳은 관리되지 않는 공간이라고 판단한다. 관리 안 되는 공간에서는 자기 글도 스팸 취급받을까 봐 참여를 꺼린다. 스팸 방어는 단순히 광고를 지우는 일이 아니라, 이 공간이 잘 관리되고 있다는 신뢰를 지키는 일이었다.
그래서 나는 스팸 방어를 사후 청소가 아니라 사전 예방으로 접근했다. 이미 올라온 스팸을 지우는 것은 늦다. 사람들이 이미 봤고, 검색 엔진도 이미 긁어갔을 수 있다. 진짜 목표는 스팸이 애초에 올라오지 못하게 문 앞에서 막는 것이었다. 방어선은 글이 등록되기 전에 작동해야 의미가 있었다.
2. 문 앞에서 거르는 장치들
첫 번째 방벽은 사람인지 기계인지를 구분하는 것이었다. 대부분의 스팸은 자동 프로그램이 뿌린다. 그래서 사람은 쉽게 통과하고 기계는 걸리는 관문을 두면 상당수를 막을 수 있다. 나는 사람에게는 거의 부담이 없지만 자동 프로그램에게는 넘기 어려운 검증 장치를 회원 가입과 글쓰기 길목에 배치했다. 사람됨을 증명하는 이 최소한의 관문이 자동 스팸의 대부분을 차단했다.
다만 이 관문에는 함정이 있었다. 너무 까다로우면 진짜 사람까지 막는다. 실제로 나는 이 검증 장치가 특정 환경의 정상 사용자를 걸러내 로그인이나 글쓰기를 막는 사고를 겪은 적이 있다. 방어가 과하면 스패머보다 진짜 사용자를 더 많이 쫓아낸다. 그래서 나는 방벽의 강도를 위협 수준에 맞춰 조절했다. 평소엔 느슨하게, 공격 징후가 보이면 강하게.
두 번째 방벽은 속도 제한이었다. 사람은 글을 아무리 빨리 써도 한계가 있다. 그런데 기계는 초당 수십 개를 쏟아낸다. 그래서 나는 일정 시간 안에 한 사용자가 할 수 있는 행동의 수에 상한을 뒀다. 개념적으로는 같은 주체가 짧은 시간에 반복하는 요청을 세어 임계를 넘으면 잠시 차단하는 방식이었다. 도배와 무차별 대입 공격이 이 한 겹에서 크게 꺾였다.
세 번째 방벽은 내용 자체를 보는 것이었다. 알려진 스팸 패턴, 수상한 링크 뭉치, 같은 문구의 반복 같은 신호를 잡아냈다. 완벽하진 않지만 명백한 것들은 걸렀다. 다만 지난 편에서 말했듯 내용 판단은 기계에게 최종 결정권을 주지 않고, 의심스러운 것을 앞에 놓아 사람이 확인하게 하는 선에서 썼다. 여러 겹의 방벽이 각자 다른 각도에서 스팸을 걸러냈다.
3. 어뷰징은 스팸보다 교묘하다
스팸이 노골적이라면 어뷰징은 교묘하다. 어뷰징은 정상적인 기능을 악용해 부당한 이득을 취하는 것이다. 추천을 조작해 자기 글을 띄우거나, 신고를 무기로 남을 괴롭히거나, 시스템의 허점을 파고들어 지표를 부풀리는 행위들이다. 이건 명백한 광고처럼 딱 잘라 걸러내기 어렵다. 정상 행동과 악용의 경계가 흐릿하기 때문이다.
가장 흔한 어뷰징은 자기 콘텐츠를 인위적으로 띄우는 것이었다. 자기 글에 추천을 몰아주거나, 조회 수를 부풀리거나, 여러 방법으로 순위를 조작한다. 앞서 정렬 편에서 통계적 신뢰 구간이 소수의 몰표에 흔들리지 않는다고 했는데, 그 설계가 여기서 방어로 작동했다. 알고리즘 자체가 어뷰징에 둔감하도록 만드는 것이, 사후에 잡아내는 것보다 근본적이었다.
또 하나 신경 쓴 것은 지표를 세는 규칙의 허점이었다. 예를 들어 조회 수를 방문마다 올린다면, 새로고침을 반복해 조회 수를 부풀릴 수 있다. 나는 이런 허점을 하나하나 점검하며 악용 가능성을 줄였다. 어떤 지표든 그것이 보상과 연결되는 순간, 누군가는 반드시 그 지표를 조작하려 든다. 지표 설계에는 늘 이 악용 시나리오를 함께 상상해야 했다.
어뷰징 방어의 핵심 원칙은, 정상 사용자를 불편하게 하지 않으면서 악용만 걸러내는 것이었다. 방어가 과하면 정상 사용자가 죄인 취급받는다. 그래서 나는 대부분의 방어를 사용자 눈에 보이지 않게 뒤에서 돌아가게 했다. 착한 사람은 아무것도 못 느끼고, 나쁜 사람만 벽에 부딪히는 것이 이상적인 방어였다.
4. 방어는 겹으로 쌓는다
스팸과 어뷰징 방어에서 내가 얻은 가장 큰 교훈은, 단일 방벽은 반드시 뚫린다는 것이다. 아무리 정교한 관문도 시간이 지나면 우회법이 나온다. 그래서 방어는 하나의 완벽한 벽이 아니라, 여러 겹의 불완전한 벽으로 쌓아야 했다. 한 겹이 뚫려도 다음 겹이 걸러내고, 그것도 뚫리면 또 다음 겹이 잡는 식이었다.
나는 방어를 층층이 배치했다. 가장 바깥에는 네트워크 수준에서 비정상적인 트래픽을 걸러내는 층이 있었다. 그 안쪽에는 사람됨을 검증하는 관문, 또 안쪽에는 속도 제한, 그다음에 내용 검사, 마지막으로 사람의 신고와 모더레이션이 있었다. 각 층은 서로 다른 종류의 위협에 강했다. 이 다층 방어가 어느 하나에만 의존하는 것보다 훨씬 튼튼했다.
다층 방어의 또 다른 장점은 각 층을 가볍게 유지할 수 있다는 것이었다. 한 층이 모든 걸 막으려 하면 그 층이 과도하게 무겁고 까다로워진다. 하지만 여러 층이 조금씩 나눠 막으면, 각 층은 자기가 잘하는 것만 하면 됐다. 부담이 분산되니 정상 사용자의 불편도 줄었다. 방어의 부담을 나누는 것이 방어를 지속 가능하게 만들었다.
그리고 나는 방어를 완성이 아니라 과정으로 받아들였다. 새로운 공격은 계속 나오고, 방어는 늘 한 발 늦다. 그래서 나는 방어를 한 번 세우고 잊는 게 아니라, 공격 패턴을 관찰하며 계속 조정했다. 이번 달 막은 수법이 다음 달엔 안 통할 수 있었다. 방어는 끝나지 않는 술래잡기였고, 그 사실을 받아들이는 것 자체가 방어의 일부였다.
5. 방어와 개방 사이
스팸과 어뷰징을 막다 보면 커뮤니티를 꽁꽁 걸어 잠그고 싶은 유혹이 생긴다. 모두를 잠재적 범죄자로 의심하고, 모든 행동에 검증을 요구하는 것이다. 하지만 그건 커뮤니티의 자살이었다. 방어가 개방을 이기면, 나쁜 사람은 어차피 우회하고 착한 사람만 문 앞에서 지쳐 돌아간다. 방어의 목적은 커뮤니티를 지키는 것이지 커뮤니티를 요새로 만드는 게 아니었다.
그래서 나는 늘 방어와 개방의 저울을 의식했다. 새 방벽을 세울 때마다, 이것이 막는 나쁜 행동과 이것이 불편하게 하는 착한 행동을 견줬다. 착한 사람 백 명을 막아 나쁜 사람 한 명을 잡는 방벽은 나쁜 방벽이었다. 반대로 착한 사람은 거의 못 느끼는데 나쁜 사람은 확실히 걸리는 방벽이 좋은 방벽이었다. 이 비율이 방어 설계의 나침반이었다.
결국 좋은 방어는 보이지 않는 방어였다. 정상적으로 활동하는 사람은 이 커뮤니티에 방어선이 몇 겹이나 깔려 있는지조차 모른다. 그들은 그저 스팸 없는 깨끗한 공간을 누릴 뿐이다. 방어가 사용자 눈에 띄기 시작하면, 그건 방어가 과하거나 어설프다는 신호였다. 침묵하는 방어가 가장 좋은 방어였다.
이번 편의 어뷰징 방어에서 반복해 등장한 위협이 하나 있다. 한 사람이 여러 계정을 만들어 표를 조작하고 여론을 왜곡하는 것이다. 이 유령 계정 문제는 워낙 뿌리 깊어 따로 다룰 가치가 있다. 다음 편에서는 중복 계정과 여론 조작, 즉 한 명이 여럿인 척하는 위협을 어떻게 막았는지 집중해서 이야기하겠다.