Browsing Tag

wordpress

IT/Linux/Kubernetes

WordPress 지속적인 스팸 댓글로 인한 조치 (feat. 웹 사이트를 직접 운영해야하는 이유)

안녕하세요. ManVSCloud 김수현입니다.

오늘은 ManVSCloud 블로그 운영 중 지속적인 스팸 댓글을 어떻게 처리할까 고민을 했던 부분을 공유하고자 합니다.

WordPress에서는 많은 무료 플러그인이 제공되지만 플러그인을 사용하지 않고 해결하고 싶었고 무엇보다 직접 서버를 운영하는만큼 내가 할 수 있는 것이라면 내가 해결하고 싶다라는 마음이 컸습니다.

(막상 고민하고 해결하기까지 2시간도 걸리지 않았습니다.)


스팸 댓글


Web Log

먼저 스팸 댓글이 쓰여진 시간작성자 IP를 참고하여 웹 로그와 비교하였습니다.

웹 로그를 살펴보니 해당 스팸 댓글은 request : POST, http_user_agent : python-requests를 공통적으로 남긴다는 것을 알 게 되었습니다.

(정상적인 댓글에서는 user_agent가 python-requests인 경우는 본 적이 없다. 주로 웹 크롤링할 때 사용됐던걸로 안다.)

45.134.225.36 - 10.0.33.10 - - [16/Aug/2022:17:39:48 +0900] "POST /?p=989 HTTP/1.1" 200 89 "-" "python-requests/2.27.1"
192.42.116.14 - 10.0.13.10 - - [16/Aug/2022:21:14:24 +0900] "POST /?p=989 HTTP/1.1" 200 89 "-" "python-requests/2.27.1"
185.243.218.41 - 10.0.13.10 - - [16/Aug/2022:23:58:19 +0900] "POST /?p=989 HTTP/1.1" 200 89 "-" "python-requests/2.27.1"

또한 데이터베이스를 확인해보면 워드프레스에서 댓글이 관리되는 테이블은 wp_comments 입니다.

해당 테이블을 조회해보니 comment_agent 필드에서도 python-requests 가 확인됐습니다.


WebDetector

자, 이제 웹 로그에서 확인한 정보를 이용해서 탐지하고 차단 그리고 댓글 정리까지 하도록 할 것입니다.

먼저 스크립트를 관리할 디렉토리를 먼저 생성해주었습니다.

mkdir -p /usr/local/webdetector/{bin,include,logs,tmp}

  • bin

1. env
: 모든 스크립트에서 공통적으로 사용될 환경 설정이 들어가는 파일입니다.

#!/bin/bash

##########################################
export NOW=$(date +"[%F %T]")
export WD=/usr/local/webdetector
export TEMP=$WD/tmp
export LOG=$WD/logs
export INCLUDE=$WD/include
export WEBLOG=/<웹로그 경로>/manvscloud.access.log
export DBPWD=<DB 패스워드>
##########################################

2. start.sh
: 스크립트 실행 파일입니다.

#!/bin/bash

#######################################################
source /usr/local/webdetector/bin/env
########################################################

main() {

$INCLUDE/log_detector.sh

}

main

  • include
    (include 디렉토리에는 각종 기능들을 추가하여 관리하기로 하였습니다.)

1. log_detector.sh
: 웹 로그에서 이상 반응을 탐지합니다.
기존 detectCount보다 수치가 높아지면 deny.sh을 실행시켜 IP를 선차단 조치합니다.

#!/bin/bash

CUR_LOG=`cat $WEBLOG | grep POST |  grep "python-requests" | awk '{print $1}' | sort | uniq | wc -l`
COUNT=`cat $TEMP/detectCount`

detector() {

if [ $CUR_LOG -gt $COUNT ]; then

echo "$NOW 이상 로그가 감지 되었습니다." >> $LOG/drop.log

$INCLUDE/deny.sh

fi

}

detector

2. deny.sh
: 웹 로그에서 해당하는 조건에 탐지된 IP를 차단합니다.

#!/bin/bash

############################################################################################################################
ATKIP=(`cat $WEBLOG | grep POST |  grep "python-requests" | awk '{print $1}' | sort | uniq`)
iptables -nL | grep DROP | awk '{print $4}' | sort | uniq | grep -vE "[a-Z]|0.0.0.0/0" > $TEMP/drop_ip_list.txt
OVLLIST=`cat $TEMP/drop_ip_list.txt`
############################################################################################################################

dropIP() {

    for i in ${ATKIP[@]};

    do

        OVLIP=`echo $OVLLIST | grep $i | wc -l`

        if [ $OVLIP == 1 ]; then

            echo "$NOW $i IP는 이미 차단되어 있습니다."

        else

            iptables -I INPUT -s $i -j DROP
            echo "$NOW $i IP가 차단 되었습니다." >> $LOG/drop.log
            $INCLUDE/db.sh
        fi

    done

##############################################################################################################
cat $WEBLOG | grep POST |  grep "python-requests" | awk '{print $1}' | sort | uniq | wc -l > $TEMP/detectCount
rm $TEMP/drop_ip_list.txt
##############################################################################################################

}

dropIP

3. db.sh
: DB에서 조건에 해당하는 댓글을 제거합니다.

#!/bin/bash


delSPAM() {

SPAMCOMMENT=(`mysql -uusername -p$DBPWD -D dbname -e "select comment_ID from wp_comments where comment_agent like 'python-requests%';" | awk '{print $1}' | grep -v [a-Z]`)

    for i in ${SPAMCOMMENT[@]};

    do

        mysql -uusername -p$DBPWD -D dbname -e "delete from wp_comments where comment_ID=$i;"

    done

}

delSPAM

  • logs

1. drop.log
: 차단된 IP의 로그입니다.

[2022-08-17 02:49:34] 185.220.100.241 IP가 차단 되었습니다.
[2022-08-17 02:52:55] 37.120.185.177 IP가 차단 되었습니다.
[2022-08-17 07:50:01] 185.7.33.146 IP가 차단 되었습니다.

  • tmp

1. detectCount
: 탐지 카운트입니다. 탐지가 될 때마다 수치가 달라집니다.

0

마지막으로 /etc/crontab에 아래와 같이 스크립트를 5분마다 실행되도록 해두었습니다.
5분마다 이상 로그가 있는지 지속적으로 탐지하게 될 것입니다.

## webdetector
*/5 * * * * root /usr/local/webdetector/bin/start.sh

또한 제 웹 서버는 Logrotate를 이용하여 로그가 관리되고 있습니다.
즉, 로그가 압축되고 분할될 때 기존 detectCount와 수치가 달라지게 됩니다.

그렇기 때문에 logrotate 설정에서 Logrotate가 실행될 때 detectCount도 초기화되도록 설정하였습니다.

/var/log/nginx/*.log {
    weekly
    rotate 5
    compress
    delaycompress
    missingok
    create 644 root root
    dateext
    postrotate
            if [ -f /var/run/nginx.pid ]; then
                    kill -USR1 `cat /var/run/nginx.pid`
                    echo "0" > /usr/local/webdetector/tmp/detectCount
            fi
    endscript
}

이제 제 블로그는 user agent가 python-requests인 경우에는 탐지 후 차단하도록 설정되었습니다.

하지만 python-requests에 대해서만 탐지되기때문에 이후에 추가적으로 발견되는 이상 증상은 스크립트에 계속 추가적으로 업데이트되고 나중에는 따로 스크립트가 분리될 수도 있을 것이라 예상됩니다.


Personal Comments

그냥 플러그인 쓰면 간편합니다.
이미 존재하는 툴들이나 IPS, WAF 등을 사용하면 됩니다.
대부분 이러한 스팸 댓글들은 외국에서 쓰여지니 국내에서만 댓글을 쓸 수 있게하면 굳이 신경쓸 일이 줄어듭니다.

사용하면 좋죠. 실제 금전이 오가는 운영 환경에서는 당연히 고려해야하는 부분이구요.

그런데 이렇게 번거롭게 직접 확인하고 스크립트를 만들어가며 차단하는 이유는 어떤 유형으로 스팸댓글이 쓰여진다거나 어떤 공격이 존재하는지 또 댓글은 어느 DB에 저장되는지 공부할 수 있기때문입니다.

어차피 제 블로그는 주기적으로 백업되고 있고 다운되어도 돈 받고 운영되는 서비스가 아니기때문에 제 마음대로 테스트할 수 있는 좋은 환경입니다.

웹 서비스는 가장 기본입니다.
전 직장에서 제 사수였던 Linuxer님이 WordPress를 이용해서 블로그 운영을 직접해보라고 하셨었는데 이게 많이 도움이 됐습니다.

free tier에서 최고 효율을 낼 수 있는 구성을 생각해보고 인스턴스 타입을 높이면 결국 비용으로 이어지기때문에 서버 내에서 최적화를 고민하게되고 이후에 AWS에서 네이버 클라우드로 이전도 하고 각종 공격이 생기면 직접 고민하고 해결하게 됩니다.

다시 한 번 Linuxer님에게 감사하는 마음을 가지며 다른 분들에게도 직접 웹 사이트 하나 정도는 운영해보는 것을 권장드립니다.

긴 글 읽어주셔서 감사합니다.