/Comments매크로, /Thread매크로를 사용하여 쉽게 커멘트를 달 수 있게 했더니만 스팸이 기승을 부리는 터라...
이미 2006년8월에 ext1.97에서 구현을 한번 했었지만, 이 구현은 정상적인 사용자들이 매번 "NOSPAM"등의 문자열을 필드에 추가러 넣어줘야 하니 아무래도 불편하다. 그래서 새로 고쳐보았다. (2007년1월8일. ext1.101)
기본 아이디어는 [스팸 코멘트, 트랙백을 막기 위한 CCode 와 TCode 플러그인 from AlogBlog]에 있는 CCode에서 얻었다. 그런데 주인장은 그 CCode 루틴을 분석해서 똑같이 적용할 만한 내공이 안 되는 터라, 좀 간단하게 바꿔 보았다.
코멘트 입력 폼에 히든 필드(필드 이름은 ccode) 하나를 추가하여, 정상적으로 웹브라우저를 통해 코멘트를 입력할 때는 그 필드값이 그대로 전송이 되므로 문제가 없지만, 스팸 봇이 POST를 시도할 때는 그 필드가 누락되었기 때문에 스팸으로 간주하여 무시하도록 한다.
이 ccode의 값은 어떻게 결정되느냐 하면, 그 페이지 이름과 그 날의 날짜를 crypt 함수에 넣어서 만든다. 좀 더 정확히는 "페이지 이름 문자열의 길이"와 "월-일" 형태의 문자열을 이어서 (ex. "Test"페이지, 2007년 1월 9일에 해당하는 문자열은 "41-9") 사용한다.
그러니 스팸 봇이 웹페이지를 읽고 소스코드를 분석해서 ccode값을 얻어내는1 수고를 한다 하더라도, 그 값은 그 페이지(와 이름의 길이가 같은 페이지들)에 대해서만 그날 하루와 다음날까지만 유효하다. (다음날은 왜 유효하냐 하면... 정상적인 이용자가 23:55에 웹페이지를 띄운 후에 코멘트를 작성하고 다음날 00:05에 확인 버튼을 누를때 스팸으로 취급되는 것을 막기 위해서이다2)
당연히... 직접 웹브라우저로 페이지 띄워놓고 Ctrl+V로 붙여놓고 확인 눌러서 스팸 올리는 경우는 못 막는다. 특히나 조프님이나 Nyxity님 홈페이지에 달리는 한글 광고들은 봇을 쓰는건지 직접 다는 건지를 잘 모르겠다.
봇은 얼마나 자주 페이지 파싱, 분석, 값 얻어내기 등을 할까? 알록블록에서는 임의의 블로그에 스팸 코멘트를 전송하기 위해, 이런 비싼 자원(시간+CPU)을 낭비하면서 시도하진 않으리라는 계산이 깔려있습니다라고 하는데, 주인장은 반대로 "스패머들이 그런 고민까지 할 것인가"라는 의문이 듭니다. 어쩌면 스팸 한 번 보낼때마다 페이지 파싱을 하고 있는지도? -_-;
subaction_comments{
...
my$anchor;
my$match = 0;
# 이 줄은 스팸과는 관계없는데 덩달아 수정returnif (!&ValidIdOrDie($id));
# ccode - 이 단락 추가my$ccode = &GetParam("ccode", "");
my ($code_today, $code_yesterday);
$code_today = &simple_crypt(length($id).substr(&CalcDay($Now),5));
$code_yesterday = &simple_crypt(length($id).substr(&CalcDay($Now - 86400),5));
if (($ccodene$code_today) && ($ccodene$code_yesterday)) { # spam&ReportError("SPAM comment");
return;
}
# 여기까지# 금지단어 처리는 여기서 먼저if (my$bannedText = &TextIsBanned($newcomments)) {
print&GetHeader("", T('Editing Denied'),"");
printTs('Editing not allowed: text includes banned text');
print" [$bannedText]";
print"\n<br><hr noshade size=1><p><strong>". T('This is the text you submitted:').
"<br>". T('(Copy the text, go back with your browser, paste the text, and edit again please)').
"</strong><p>".
&GetTextArea('text', $newcomments, &GetParam("editrows", 20), &GetParam("editcols", 65)).
"<p>\n";
print&GetCommonFooter();
return;
}
# 블로그 지원을 위한 꽁수
...
}
TrackBack의 경우도 동일한 아이디어로 처리한다. 트랙백 주소에 "tc=******" 라는 항목이 추가되었고, 이 tc의 값은 페이지 이름과 그 날의 날짜에 의해 달라진다. 트랙백 주소는 당일날과 그 다음날까지 유효하므로, 봇이 페이지를 읽어서 트랙백 주소를 얻어내더라도, 그 페이지에 한해서 그 다음날까지만 보낼 수 있다. (봇이 스팸 테러를 하기에는 충분히 넉넉한 기간이긴 하다 -_-;)
이제는 특정 페이지의 트랙백 주소가 날짜에 따라서 달라진다는 얘기고, 따라서 페이지의 메타 정보에 트랙백 주소를 넣거나 하는 경우에 문제가 됩니다. (2007.1.8 현재 이곳의 위키에는 그런 기능이 없습니다만) 즉 그런 메타정보를 읽어서 자동으로 트랙백을 쏴줄수 있는 그런 프로그램을 통해서 트랙백을 보내는 것은 힘들어진다는 거죠. alogblog에서도 언급하고 있고, 거기서는 일단 트랙백을 받아서 DB에만 저장하고 웹페이지에는 반영을 하지 않은 다음에, 관리자가 직접 판단해서 반영 여부를 결정하게 하였더군요. 여기서는... 그때가 되면 생각하기로 합시다. :-)
당연히 방문자가 그 값을 알 수 있을 리 없으니, 그냥 텍스트로 옆에 설명이 달린다. 즉 받아 적으면 된다.
사용예는 아래 사용자 의견 항목을 보면 쉽게 알 것이고
스팸방지 키워드는 대소문자 구분을 하지는 않는다.
부작용
커멘트 다는 사람 입장에서는 매우 귀찮다. :-/
스팸방지 필드를 실수로 누락한 채로 확인 버튼을 누르면 썼던 글이 다 날아갈 것이다. :-$
wiki.pl 수정
################## added by gypark### 패치를 위해 추가된 환경설정 변수use varsqw( $UserGotoBar $UserGotoBar2 $UserGotoBar3 $UserGotoBar4 ... $AntiSpam # 추가);
##################
comments 매크로의 경우 스팸 방지 필드가 추가되면 한 줄에 너무 길게 필드가 배치되기 때문에, 기본으로 두 줄로 배치되도록 고쳤다. 이름 필드의 길이도 조절. 따라서 MacroComments 함수의 경우 이것저것 바뀐게 많아 일일이 설명을 달지 못하니 아래 diff 를 보고 참고할 것.
ext1.101 - 완전히 내용이 바뀜. 사용자가 추가로 코드를 넣는 방식을 없애고, 자동으로 확인 필드를 삽입하도록 함.
ext1.102 - 트랙백 관련 코드에도 적용.
코멘트든 트랙백이든 CalcDayNow 와 CalcDay 가 혼용되는게 보기 안 좋아서 CalcDay 를 쓰도록 고쳤습니다. 이건 뭐 굳이 다시 고칠 필요는 없음.
ext1.102a - ccode와 tcode를 생성할 때 사용하는 인자로, "페이지 이름의 길이"와 오늘 날짜를 나타내는 문자열 중 "월-일" 부분을 합쳐서 사용하도록 수정.
ext1.103 - 페이지 수정 관련 코드에도 적용.
ext1.103a - 커멘트의 중간에 등록 금지 단어가 있는 경우에, 커멘트 저장은 안 되면서 BlogRc 페이지는 고쳐지는 문제 수정
ext1.109c
사용자가 본의 아니게 금지어를 포함한 코멘트를 달았을때, 작성했던 내용을 전부 날려버리는 문제를 수정. (자세한 내용은 /등록금지단어의 추가 업데이트 내역 참조)
이 패치로 얼마나 스팸을 막을 수 있는지 로그를 남겨서 보려고 코드를 수정해 보았습니다. 이건 배포용 소스에는 넣기가 그래서 여기에 수정 내용을 적어 둡니다. 제 위키와 조프위키, 바벨 셋 중에서 가장 스팸에 시달리던 곳이 바벨인지라, 바벨에 적용해 보고 일주일쯤 후에 로그파일을 한번 살펴보면 어떨까 싶어요.
코멘트와 트랙백의 경우는, 원래 있는 소스를 고치지 말고, action/ 디렉토리 아래 있는 comments.pl과 tb.pl을 myaction/ 디렉토리로 복사한 후에, myaction 아래 있는 걸 고치면 편하겠습니다. 아니면 그냥 아래 링크에서 다운로드해서 myaction 디렉토리에 넣으셔도 됩니다. 나중에 myaction 아래 있는 걸 지우면 원상복귀 되니까요. 페이지 수정의 경우는 wiki.pl 을 직접 고쳐야 하는 터라 그러지 못하겠네요.
코멘트는 myaction/comments.pl : comments.txt.txt (뒤의 .txt.txt 를 .pl로 바꿀것)
if (($ccodene$code_today) && ($ccodene$code_yesterday)) { # spam### begin of logging routine - 이 단락 추가my ($authorAddr) = $ENV{REMOTE_ADDR};
my ($timestamp) = CalcDay($Now) . "" . CalcTime($Now);
if (open (LogFile, ">>$DataDir/__spam__")) {
printf (LogFile"C|%-19s|%-15s|%s\n", $timestamp, $authorAddr, $id);
closeLogFile;
}
### end of logging routine&ReportError("SPAM comment");
return;
}
트랙백은 myaction/tb.pl : tb.txt.txt (뒤의 .txt.txt 를 .pl로 바꿀것)
if (($tcodene$code_today) && ($tcodene$code_yesterday)) { # spam### begin of logging routinemy ($authorAddr) = $ENV{REMOTE_ADDR};
my ($timestamp) = CalcDay($Now) . "" . CalcTime($Now);
if (open (LogFile, ">>$DataDir/__spam__")) {
printf (LogFile"T|%-19s|%-15s|%s\n", $timestamp, $authorAddr, $id);
closeLogFile;
}
### end of logging routine&SendTrackbackResponse("1", "SPAM trackback");
return;
}
페이지 수정은 wiki.pl 수정
if (($ecodene$code_today) && ($ecodene$code_yesterday)) { # spam### begin of logging routinemy ($authorAddr) = $ENV{REMOTE_ADDR};
my ($timestamp) = CalcDay($Now) . "" . CalcTime($Now);
if (open (LogFile, ">>$DataDir/__spam__")) {
printf (LogFile"E|%-19s|%-15s|%s\n", $timestamp, $authorAddr, $id);
closeLogFile;
}
### end of logging routine&ReportError("SPAM editing");
return;
}
세 군데 다 완전히 동일한 코드이고 딱 하나 printf 에 보면 따옴표 직후에 C, T, E만 다릅니다. 만일 스팸이 이 패치에 의해 걸러지게 되면 위키 데이타가 있는 디렉토리($DataDir)에 __spam__ 이란 파일에다가 아래와 같이 로그를 남길 겁니다.
T|2007-1-10 12:13 am |220.80.107.90 |연습장 <-- T는 트랙백을 의미, 그 다음은 날짜, IP주소, 페이지
트랙백에도 마찬가지로 응용해서, 트랙백 주소에 이 ccode값을 포함하면 될 것 같습니다. 그러나 스팸봇이 매번 페이지를 읽고 트랙백주소를 얻어간다면 무용지물이겠지요. 그래서 일단은 그제 올린 수정(트랙백 주소의 action값만 바꾼)이 얼마나 잘 통하는지를 관찰을 좀 해볼까 싶어요. 제일 스팸트랙백에 시달리던 바벨의 도서관이 마침 어제 그 수정을 했으니, 얼마나 오래 견디는지 좀 구경했으면 (^^;) 좋겠네요.
오후쯤에 트랙백에도 적용한 코드를 올리긴 하겠습니다만, 그 트랙백 패치는 바벨에 적용하는 건 며칠 미뤄주시면 좋겠습니다. 이 페이지에 있는 코멘트 패치는 바로 적용하셔도 될 듯. 딱히 부작용은 없을 것 같네요. 혹시 구현에 문제가 있다면 조프님이 보고 발견해줄거라 기대를... ^^;
comments.pl 전체에서 그 두 변수가 쓰이는 곳이 없다면 없어도 됩니다. anchor는 스레드 댓글을 달았을때 바로 그위치로 이동하는 것이고, match는 comment 매크로가 없는 페이지에 스팸봇이 코멘트를 달았을때 blogrc 페이지만 갱신되는 것을 막으려고 넣은 것이니 둘 다 있는 게 나을 듯 합니다. 그냥 comments.pl 은 그대로 가져다 쓰셔도 되지 않을런지. (따로 수정하신 게 있다면 그 부분은 옮겨 주셔야겠지만)
아 제 바벨에서 코멘트를 달려고 하니 금지단어가 있어서 수정불가라고 하는데 금지단어는 페이지에 없는 단어이거든요. 근데 모노로그 코멘트란에 보면 다양한 스팸코멘트들이 달려고 했던 시도들이 보이더군요. 그래서 그런 기록이 남아서 그런게 아닐까 멋대로 추측을 하는데, 꽤 당황했어요.
알록블록의 구현처럼 자바스크립트를 써서 code를 감추는 방법을 추가로 구현한다 하더라도,
1) 스팸이 줄어도 정말 그 감추기 덕분에 스팸이 주는 것인지 확인하기 힘들고
2) 결정적으로 페이지 편집이나 트랙백은 몰라도, 횡설수설 같이 한 페이지 내에 코멘트창이 수십개 들어있는 곳에서는 매번 서버 쪽에서 감추기 코드를 생성하고 클라이언트 쪽에서는 자바스크립트가 그 계산을 해야 한다는 게 참 성능에 저하가 될 것 같습니다.
아... 모노로그 하위 페이지인 BlogRc 페이지에 문제가 있었네요. 스팸트랙백을 예전에 받으면서 거기에 그 트랙백 내용이 적혔고, 그 이후에 금지단어 목록에 그게 추가되니까, 이후에는 정상적인 코멘트를 달 경우 코멘트는 제대로 저장이 되는데, BlogRc를 갱신하려고 하는데 이미 내용에 금지단어가 들어가 있으니 거기서 문제가 되었네요. 그 라인을 지워주었으니 이제 잘 될 겁니다 ^^ BlogRc 페이지도 종종 들여다보면서 스팸 찌꺼기를 지워주시면 좋을 듯 하네요. (애초에 블로그 흉내 내려고 편법으로 우겨넣은 거다보니 관리에도 좀 신경쓸게 많군요 ^^;)
뭐 이곳도 하루에 하나 이상 꼬박꼬박 받고 있다보니 -_-; 이거 영 잘 안 돌아가는 것 같기도하고... 한글 스팸들은 매일 사이트 직접 찾아와서 폼에 데이타 넣고 submit하는 것 같기도 하고... 저도 잘 모르겠어요, 딱히 날짜 때문에 제대로 동작하지 않는 건 아닌것 같은데.
언제부턴가 매일 여기에 걸리는 스팸 트랙백이 300여개에 달해서, 아주 뿌듯해하고 있었는데... 로그를 보니 이게 다 구글의 IP였다 -_-;;; 스팸을 남기려 한 게 아니라 URL이 있으니 그냥 크롤링 하려고 했었나본데, 트랙백 주소는 링크가 아니라 텍스트로 남아 있는데 이걸 왜....
각주: 1. 값 자체는 html코드에 그대로 드러나 있어서, 파싱만 하면 금방 얻어낼 수 있다. alogblog에서 하는 것처럼 자바스크립트를 써서 값을 흐트려놓지는 않았음 2. 코멘트를 작성하는데 24시간 이상을 들이는 사용자가 있다면... 정말 힘들게 작성한 코멘트가 스팸 취급 받아 날아가 버리겠지만... 설마 그런 경우는 없겠지.