PostgreSQL

PostgreSQL: DDoS(Distributed Denial of Service) 공격 방어

dewstream 2025. 2. 15. 17:00

※ Defending against DDoS attacks from the perspective of a PostgreSQL DB server in a 3-tier architecture.

 

안녕하세요. 듀스트림입니다.

 

발렌타인 데이 저녁, 제가 관리하는 DB 서버가 DDoS 공격을 당했습니다.

 

아키텍처는 아래 그림처럼 Web, WAS, DB 3-tier로 구성되어 있습니다.

[그림 1] 3-tire Architecture

 

사실 이런 구조에서 DDoS 공격을 받았을 때, DB 서버에서 할 수 있는 조치는 제한적인 임시 방어뿐입니다.

이유는 HTTP Flood 방식으로 공격이 들어왔기 때문에 DB 서버에서는 모든 요청이 WAS에서 온 것으로 보입니다.

그렇기 때문에 DB 서버 자체에서 IP나 클라이언트 프로그램을 식별하여 종료시키거나 접속을 제한하기는 매우 어렵습니다.

 

하지만 일단 서버 다운은 막아야 하기 때문에 아래와 같이 조치하였습니다.


-- Step 1: 데이터베이스, 사용자별 세션 수 집계
-- → 각 데이터베이스와 사용자별로 현재 연결된 세션 수를 확인하여, 비정상적인 과다 연결을 식별하기 위함입니다.
SELECT datname, usename, count(*) 
FROM pg_stat_activity 
GROUP BY 1, 2 
ORDER BY 3 DESC;


-- Step 2: 다량의 세션이 연결된 데이터베이스에 신규 연결 차단
-- → DDoS 공격의 확산을 막기 위해 해당 데이터베이스에 신규 연결을 임시로 차단합니다.
UPDATE pg_database 
SET datallowconn = false 
WHERE datname = 'database_name';


-- Step 3: 공격 의심 사용자 식별
-- → pg_stat_statements를 활용하여, 공격 대상 데이터베이스에서 쿼리 호출이 많은 사용자를 식별합니다.
SELECT pg_catalog.pg_get_userbyid(s.userid) AS username,
       s.calls,
       s.mean_exec_time,
       s.query
FROM pg_stat_statements s
JOIN pg_database d ON s.dbid = d.oid
WHERE d.datname = 'database_name'
ORDER BY 2 DESC
LIMIT 10;


-- Step 3-1: 쿼리 통계 뷰 초기화 (필요시)
-- → 기존 통계 데이터를 초기화하여 최신 상태의 호출 현황을 재확인할 수 있습니다.
SELECT pg_stat_statements_reset();


-- Step 4: 공격 의심 사용자의 연결 정보 조회
-- → 공격 의심 사용자의 현재 연결 상태(세션 정보, 클라이언트 주소 등)를 상세히 파악합니다.
SELECT pid, datname, usename, application_name, client_addr, state
FROM pg_stat_activity
WHERE usename = 'role_name';


-- Step 4-1: 해당 사용자에 대한 커넥션 수 제한 적용
-- → 동시에 생성 가능한 세션 수를 제한하여 추가 공격이나 자원 소모를 방지합니다.
ALTER ROLE role_name CONNECTION LIMIT 10;


-- Step 5: 공격 의심 사용자의 기존 연결 종료
-- → 현재 활성화된 세션 중, 해당 사용자의 연결을 종료하여 악의적 활동을 차단합니다.
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE usename = 'role_name'
--  AND pid <> pg_backend_pid()  -- 현재 세션 제외
;


-- Step 6: 데이터베이스 연결 차단 상태 확인
-- → 임시 차단 상태가 정상적으로 적용되었는지 점검합니다.
SELECT datname, datallowconn 
FROM pg_database 
WHERE datname = 'database_name';


-- Step 7: DMZ 설정까지 대기
-- → 네트워크 보안 조치(DMZ 구성 등)를 완료할 때까지 이 상태로 대기합니다.


-- Step 8: DMZ 설정 완료
-- → DMZ 구성 완료 후, 보안 조치가 적용되었음을 확인합니다.


-- Step 9: 데이터베이스의 연결 차단 해제
-- → 임시로 차단했던 데이터베이스의 신규 연결 허용 상태를 복원합니다.
UPDATE pg_database 
SET datallowconn = true 
WHERE datname = 'database_name';


-- Step 10: 사용자에 대한 커넥션 제한 해제
-- → 공격이 없다 판별되면 사용자에 대한 임시 연결 제한을 해제합니다.
ALTER ROLE role_name CONNECTION LIMIT -1;

물론 해당 데이터베이스와 롤을 사용하는 다른 정상 사용자들도 영향을 받을 수밖에 없는 조치입니다.

하지만 이미 DDoS 공격으로 다운 직전의 마비 상태였기 때문에 이렇게 조치하였습니다.

초당 약 200만 건의 악의적 호출이 보였는데, 빠르게 조치돼서 최소한의 피해로 막을 수 있었습니다.

모니터링의 중요성을 다시 한번 상기하게 되는 이슈였습니다.

 

그럼 오늘은 여기까지~