[Linux] Next.js 취약점에 의한 Fork Bomb 공격 대응 (Docker, Fail2ban)

인지

홈서버에서 docker로 구동중인 next.js 서버에 접속이 안되는 것을 발견했다.
단순히 프로젝트 수행 내역을 전시하기 위한 목적이어서 별다른 모니터링을 적용하지 않았기에 인지가 늦었다.


홈서버에 접속해서 해당 컨테이너의 로그를 살펴보니 다음과 같은 이상한 로그들이 무수히 찍혀있었다.

cat: can't open '/tmp/.XIN-unix/config.json': No such file or directory
 ⨯ [Error: Command failed: cat /tmp/.XIN-unix/config.json |grep user
cat: can't open '/tmp/.XIN-unix/config.json': No such file or directory
] {
  status: 1,
  signal: null,
  output: [Array],
  pid: 2414272,
  stdout: <Buffer >,
  stderr: <Buffer 63 61 74 3a 20 63 61 6e 27 74 20 6f 70 65 6e 20 27 2f 74 6d 70 2f 2e 58 49 4e 2d 75 6e 69 78 2f 63 6f 6e 66 69 67 2e 6a 73 6f 6e 27 3a 20 4e 6f 20 73 ... 22 more bytes>,
  digest: '2473913793'
}
 ⨯ [Error: x] {
  digest: 'ICAgICAgICAgICAgInVzZXIiOiAiNEI3WmJrNGpiV1hUQlRUaXJGcmY0ZmhyeGVLUk54ZnNLU29idVpuUWsxQWZqWGU3cFdxUDlwMTlxaU1yMXAxUnZBNGFmQXVIdUN2Nk4xYVpyS3F5WFZMZlE0cldVTFQiLAogICAgInVzZXItYWdlbnQiOiBudWxsLAo='
}

저 파일을 왜 접근하는지 의도를 알 수 없는 에러여서 AI에게 복붙해서 물어봤고,
악의적인 공격의 흔적이라는 답변을 들었다.


채굴기가 돌아가고 있을지도 모른다고 해서, 바로 top 명령어로 프로세스를 확인해보니 UID 1001의 이름으로 이상한 이름의 프로세스들이 무수히 많이 실행되고 있었고, CPU idle seconds가 0으로 바닥을 치고있었다.

nextjs 서버에 알 수 없는 공격이 가해졌고, 이로 인해 불결한 프로세스들이 대거 실행중이라고 정리할 수 있었다.

문제 확인

UID 1001은 호스트에 없는 UID이고, nextjs 컨테이너 내부에서 바인딩한 nextjs라는 사용자의 UID가 1001이다.
즉, 컨테이너 내부에서 생성된 프로세스들이다.


nextjs의 앞단에 nginx가 위치한다.
nginx의 access.log를 확인해보니 다음과 같은 로그가 있었다.

65.49.27.185 - - [24/May/2026:14:54:58 +0900] "POST /adfa HTTP/1.1" 500 245 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"

추측컨대, nextjs 서버에 POST /adfa 엔드포인트로 요청을 보내서 500 에러를 유도하고 그 틈으로 악성 페이로드가 실행되었을 것이다.
nextjs는 기본적으로 백엔드 서버 역할까지 수행할 수 있기에 POST 요청도 처리할 수 있다.


하지만, 기존의 로그에서는 호스트에서 서빙하는 여러 서비스 중 nextjs 서버를 향한 요청인지는 정확하게 확인할 수 없다.
높은 확률로 맞겠지만 100% 정확하게 확인하기 위해 nginx log format을 조정했다.


이후 다시 남겨진 로그는 다음과 같으며, nextjs 서버인 hi.hyeonwoo.com으로 들어온 요청임을 정확하게 확인했다.

65.49.27.185 - - [24/May/2026:16:00:13 +0900] "POST /adfa HTTP/1.1" 500 105 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" host="hi.hyeonwoo.com" to="10.10.10.51:8082" ups_status=500 request_time=0.011 ups_time=0.011

pkill -u 1001로 악성 프로세스들을 모두 내려도 POST /adfa 요청이 들어오면 다시 악성 프로세스들이 뜨기 시작했다.


확인된 내용과 추측한 내용을 토대로 문제를 다음과 같이 정리했다.

  • nextjs 서버는 POST 요청 처리 가능
  • 서버에 악성 페이로드를 포함한 POST 요청이 들어왔고, 페이로드에 심어진 스크립트가 실행되어 악성 프로세스들이 대거 실행됨(Fork Bomb)
  • 악성 프로세스들이 시스템 자원을 고갈시킴
  • 자원 고갈로 인해 서버의 정상 동작이 어려움

따라서 다음과 같이 조치하기로 했다.
  • nextjs 서버에 POST 요청이 전달되지 않도록 앞단(nginx)에서 검사
  • 악의적인 요청 감지 시 IP 차단
  • nextjs 컨테이너의 자원 사용량 제한

조치

1. Nginx 설정으로 POST 요청 차단

nextjs 서버로 향하는 도메인인 hi.hyeonwoo.com에 대한 가상호스트 설정에 다음과 같이 추가했다.

server {
    server_name hi.hyeonwoo.com;
    listen 443 ssl;

    # ...

    location / {

        # GET, HEAD 제외 모든 메서드는 405 Not Allowed 응답
        if ($request_method !~ ^(GET|HEAD)$) {
                return 405;
        }

        # ...
    }
}

2. 악의적인 요청 감지 시 IP 차단

fail2ban을 설치하여 nginx 405 에러를 감지하도록 설정했다.

nginx의 액세스 로그인 /var/log/nginx/access.log를 폴링하여 정규식에 걸리는 로그가 있는지 검사한다.
이 도메인으로의 GET, HEAD 이외의 요청은 그 자체로 악의적인 의도를 가지므로,
한번이라도 감지 시 해당 IP를 영구 차단한다.


/etc/fail2ban/filter.d/nginx-405.conf

[Definition]
failregex = ^<HOST> -.*?".*?" 405 .*? host="hi\.hyeonwoo\.com"
ignoreregex =

/etc/fail2ban/jail.d/nginx.conf

[nginx-405]
enabled  = true
filter   = nginx-405
port     = http,https
backend  = polling
logpath  = /var/log/nginx/access.log
# 한번이라도 걸리면 영구 차단
maxretry = 1
bantime  = -1

3. nextjs 컨테이너의 자원 사용량 제한

docker stats 명령으로 모니터링하며 CPU와 메모리의 사용량을 확인했고, 이를 토대로 제한 값을 결정했다.

CPU

페이지를 몇번 새로고침 해보니 보통 11%, 최대 17%까지 찍었다.
현재 호스트의 CPU 코어 수는 12개이므로, 코어 두개 분이 약 17%다.
따라서 CPU limits를 코어 두개 분인 2.0으로 제한한다.

Memory

현재 144MiB 이므로, 그 위로 가장 가까운 2진수 단위인 256MiB로 제한한다.

결과

docker compose 파일에서 limits 설정 후 컨테이너를 재시작했다.
docker inspect 명령으로 확인하니 다음과 같이 제한이 잘 걸려있다.

"Memory": 268435456,
"NanoCpus": 2000000000,

마치며

fail2ban에 의해 정상적으로 공격자 IP가 차단되고, 차단되니 IP를 바꿔서 다시 공격했지만 당연히 그 IP도 바로 차단되는 것을 확인할 수 있었다.
더이상 악성 프로세스가 실행되지도 않는다.


기본적으로 홈네트워크에 VPN 설정, 외부공개 포트 최소화 등의 조치를 취해놓았던 상황이었지만,
애플리케이션 레벨에서 공격이 들어와 호스트가 마비되니 당황스러웠다.
모니터링도 미약해서 호스트가 마비된 사실도 즉각적으로 인지하지 못했다는 점도 부끄럽다.


네트워크 진입 부인 라우터 단의 보안만 신경썼던 과거를 반성하는 계기가 되었고,
애플리케이션과 시스템 레벨에서 취할 수 있는 기본적인 보안 조치와 및 모니터링 개선을 빠른 시일 내 진행할 계획이다.