사툰사툰

Cannot GET phpMyAdmin.scripts.setup.php : Nginx에서 스팸 봇 차단하기
NGINX

Cannot GET phpMyAdmin.scripts.setup.php : Nginx에서 스팸 봇 차단하기

새벽에 phpMyAdmin, websql 등으로 서버에 접근하려는 흔적을 발견하여 대처한 기록을 담아두었습니다.

2024. 5. 7.

217

🚀 Overview

클라우드 서버에 배포를 하다 보면 웹 크롤러, 해킹 툴, 봇 등 생각보다 다양한 환경에서 접속하는 걸 확인할 수 있습니다. 얼마 전 Nest.js로 간단한 서버를 만들던 도중 다음과 같은 로그를 확인했습니다.

다양한 접속 시도 흔적들…
Cannot GET /.well-known/security.txt
Cannot GET /favicon.ico
Cannot GET /phpMyAdmin-2.11.4/scripts/setup.php
Cannot GET /db/scripts/setup.php
Cannot GET /admin/phpmyadmin/scripts/setup.txt
Cannot GET /php/scripts/setup.php
Cannot GET /_phpMyAdmin/scripts/setup.php
Cannot GET /webdb/scripts/setup.php
Cannot GET /websql/scripts/setup.php
Cannot GET /sqlweb/scripts/setup.php
Cannot GET /admin/scripts/setup.php
Cannot GET /SQL/scripts/setup.php
Cannot GET /mysqladmin/scripts/setup.php
Cannot GET /PHPMYADMIN/scripts/setup.php

웹 크롤러라면 해당 페이지의 파비콘과 index.html를 확인하거나, 해킹 툴이라면 DB와 관련된 임의의 경로로 정보를 탈취하려 노력합니다. 서버에선 이러한 접속을 굳이 받을 필요는 없으니 웹서버단에서 차단을 해봅시다. 현재 웹서버는 Nginx를 사용하기에 Nginx 기준으로 설명합니다.

대처법

image

서버 IP 경로 차단하기

우선 이 녀석들의 무지성 접근을 막아야 합니다. 서버의 IP 경로로 접속하는 경우가 대부분이죠. 서버의 IP 경로로 접근할 때 웹서버에서 응답 없음을 내려주어 도메인으로만 접속하도록 수정합니다.

IP 경로로 접속시 차단 (80, 443 포트)
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;

	ssl_certificate ...;
  ssl_certificate_key ...;

	return 444;
}

return 444nginx의 응답 없음(No Response)를 의미합니다. http/https로 접근할 때 기본값인 default_server로 받게 되면 특정 포트나 서버가 아닌 이상 해당 경로로 접근합니다. 보통 etc/nginx/site_enabled/default 파일에 기본 설정이 되어있으므로 이 부분과 중복되지 않게 설정해야 합니다.

http 포트만 막았을 때
server {
	listen 80 default_server;
	listen [::]:80 default_server;
	return 444;
}

http 포트(80 포트)만 막으려면 다음과 같이 간단하게 작성할 수 있습니다.

다음과 같은 페이지가 뜨면 성공!
다음과 같은 페이지가 뜨면 성공!

User-Agent로 차단하기

대개 악성 스팸 봇은 User-Agent에 특정 문구를 기입하거나 공백으로 남겨둡니다.

  • Expanse, a Palo Alto Networks company, searches across the global IPv4 space multiple times er day to identify customers; presences on the Internet. If you would like to be excluded from our scans, please send IP addresses /domains to:
  • SEC-SGHE900/1.0 NetFront/3.2 Profile/MIDP-2.0 Configuration/CLDC-1.1 Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1378; nl; U; ssr
  • scanner.ducks.party
  • Go-http-Client/1.x

이 밖에도 여러 문구를 집어 넣으며 굳이 자신이 스팸임을 과시하는 봇들이 있습니다(?). 이러한 문구들을 기록해두어 접속한 유저 중 해당 문구가 담긴 User-Agent가 발견되면 차단하도록 합니다.

server {
	### User-Agent가 비어 있으면 차단합니다
	if ($http_user_agent = “”) {
		return 444;
	}
  
	### 기타 악성/스팸 봇이 User-Agent로 파악이 된다면 차단합니다
	if ($http_user_agent ~* (kakaotalk|AhrefsBot|BLEXBot|DotBot|SemrushBot|Eyeotabot|PetalBot|MJ12bot|brands-bot|bbot|AhrefsBo|MegaIndex|UCBrowser|Mb2345Browser|MicroMessenger|LieBaoFast|Headless|netEstate|newspaper|Adsbot/3.1|WordPress/|ltx71) ) {
		return 444;
	}
}

인바운드/아웃바운드 확인

AWS 보안 규칙 예시
AWS 보안 규칙 예시

AWS로 배포했다면, EC2 인스턴스 내에 다른 포트가 열려있는지 확인합니다. 80 포트(http)와 443 포트(https)를 열어두고 직접 서버와 관련된 포트를 열지 않기로 합니다. 즉, EC2는 http/https로만 접속하도록 합니다.

리버스 프록시로 서버 접속하기

서버를 배포할 땐 여러 방법이 있지만 저는 Docker를 주로 활용합니다. Docker로 배포할 땐 컨테이너를 특정 포트로 띄워 놓고, Nginx 내에서 리버시 프록시로 연결 짓도록 합니다. server_name을 통해 지정한 도메인으로만 접속하도록 설정하고, 간단한 proxy header를 추가합니다.

리버스 프록시로 특정 서버 연결
server {
  listen 80;
	listen [::]:80
  listen 443 ssl;
  listen [::]:443 ssl;

  ssl_certificate .../fullchain.pem;
  ssl_certificate_key .../privkey.pem;

  server_name my_server;

  location / {
    proxy_pass http://127.0.0.1:8080;
    proxy_redirect off;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-Host $server_name;
  }
}

추가 조치

어플리케이션에서 필터링

해당 방법까지 진행해도 도메인 경로로 접속하는 무시무시한 놈들이 아직 남아있습니다. 사실 도메인은 대부분 A 태그 혹은 CHAME 태그로 해당 IP로 연결 짓는 단순한 도구라 nslookup만 활용해도 어느 도메인이 활성화됐는지 쉽게 찾을 수가 있습니다. 그렇기에 어플리케이션에서 한번 더 막아주어야 합니다.

우리가 웹서버에 Proxy Header를 추가하는 이유는 어플리케이션에서 해당 User-Agent와 HOST 정보를 파악하기 위해서입니다. isbot이라는 라이브러리를 활용하면 해당 유저가 쉽게 봇 여부인지 확인할 수 있습니다. 어플리케이션에 접속하기 전 middleware를 활용하여 해당 유저가 Bot인지 검증합니다.

botCheck.middleware.ts
@Injectable()
export class BotCheckMiddleware implements NestMiddleware {
  constructor() {}

  use(req: Request, res: Response, next: NextFunction) {
    const userAgent = req.get('user-agent');
    const isBot = isbot(userAgent);
    
    if (isBot) {
      res.status(403).send('Bots are not allowed');
    } else {
      next();
    }
}
app.module.ts
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(BotCheckMiddleware).forRoutes('*');
  }
}

참고

라즈베리파이4 설정(4) - NGINX에서 도메인으로 접속만 허용하기(IP주소 직접접속 차단) (codesarang.com)

웹서버 로그 점검 - phpmyadmin 을 .. : 네이버블로그 (naver.com)

오늘의 웹서버 공격 로그: phpMyAdmin 취약점 자동탐색 도구 - ZmEu Scanner (webhack.dynu.net)

ZmEu Scanner (tistory.com)

https://elfinlas.github.io/nginx/230401_defense_attacker/

#보안#AWS#Nest.js#EC2#Nginx#phpMyAdmin#Bot#isBot