블랙리스트 모니터링: 스크립트, API, 웹훅 알림
블랙리스트 모니터링이란 발신 IP와 도메인을 여러 DNS 차단 목록(DNSBL)에 정기적으로 조회하고, 결과가 바뀌었을 때만 알림을 보내는 것입니다. 고객이 반송 메일을 알려 온 뒤가 아니라 등재된 시점에 바로 알아차리면, 피해가 커지기 전에 원인을 찾고 해제를 요청할 시간을 벌 수 있습니다.
실용적인 방법은 두 가지입니다. 작은 dig·cron 스크립트를 직접 운영하거나, API와 웹훅을 통해 서비스에 확인을 맡기는 것입니다. 어느 쪽이든 모니터가 정확한 결과를 내는지는 두 가지 세부 사항에 달려 있습니다. 공개 리졸버를 통한 쿼리는 Spamhaus 같은 리스트에서 거부되어 잘못된 응답을 낳는다는 점, 그리고 오류 코드를 절대 등재로 계산해서는 안 된다는 점입니다.
여기서 인용하는 리스트 운영자의 규칙은 2026년 11월 기준 공식 페이지를 바탕으로 합니다. 정책은 바뀌므로, 구성에 의존하기 전에 확인하십시오. DNSBL의 작동 방식에 대한 배경 지식은 DNSBL(RBL)이란 무엇인가를 참고하십시오.
감시 대상과 조회할 리스트 정하기
먼저 실제로 감시해야 할 대상을 적어 두십시오.
- 발신 IP 주소. 메일 서버나 이메일 서비스가 실제로 메일을 보내는 주소입니다. 웹사이트와 메일은 서로 다른 서버에서 나가는 경우가 많으므로, 직접 보낸 메시지의 Received 헤더에서 주소를 확인하십시오. IPv6로도 발송한다면 그 주소도 모니터링하십시오.
- 도메인. From 주소의 도메인과 메시지 안의 링크에 등장하는 모든 도메인입니다. 도메인은 IP 리스트와는 별개인 도메인 차단 목록으로 확인합니다.
쿼리 형식은 RFC 5782에 정의되어 있습니다. IPv4는 옥텟 순서를 뒤집고 리스트의 존을 붙이므로, 192.0.2.99를 어떤 리스트에서 조회하면 99.2.0.192 뒤에 존 이름이 붙습니다. IPv6는 주소를 32개의 십육진 니블로 역순 표기합니다. 도메인은 존 앞에 그대로 붙입니다. 등재된 항목은 A 레코드(관례상 127.0.0.0/8 범위)를 반환하고, 등재되지 않은 항목은 NXDOMAIN을 반환합니다. RFC는 리스트가 등재 이유를 설명하는 TXT 레코드를 공개하는 것이 바람직하다고도 언급합니다.
수신 측마다 사용하는 리스트가 다르므로, 리스트 하나가 아니라 여러 개를 감시하십시오. 각 반환 코드의 의미는 운영자마다 다릅니다. 흔히 볼 수 있는 코드는 DNSBL 반환 코드 가이드에서 다룹니다.
공개 리졸버가 잘못된 응답을 주는 이유
직접 만든 모니터링에서 흔한 실수는 Google Public DNS(8.8.8.8)나 Cloudflare(1.1.1.1) 같은 공개 리졸버를 통해 쿼리를 보내는 것입니다. Spamhaus의 공식 DNSBL 사용 FAQ는 이때 어떤 일이 일어나는지 설명합니다.
- Spamhaus DNSBL을 무료로 이용하려면 비상업적 용도여야 하고, 쿼리가 공개 리졸버나 오픈 리졸버, 또는 일반적인(generic) 역방향 DNS를 가진 IP에서 오지 않아야 합니다.
- Google Public DNS 같은 무료 오픈 리졸버를 통하면 쿼리는 대부분의 경우 등재되지 않음(NXDOMAIN) 응답을 받습니다. 즉, 등재된 IP가 깨끗해 보일 수 있습니다.
- 공개 리졸버를 통한 쿼리는 127.255.255.254를 받을 수도 있습니다. 이는 등재가 아니라 오류 코드입니다. 마찬가지로 127.255.255.252는 DNSBL 이름의 오타를, 127.255.255.255는 과도한 쿼리 수를 뜻합니다. Spamhaus는 이 코드들을 조회 대상이 등재되었다는 의미로 받아들여서는 안 된다고 명시합니다.
- NXDOMAIN 응답을 다시 쓰는 리졸버는 대신 IP 주소를 반환하므로, 올바른 NXDOMAIN을 볼 수 없습니다.
FAQ가 가장 먼저 권하는 방법은 자체 DNS 리졸버를 운영하는 것입니다. 공개 리졸버를 계속 써야 하는 사용자에게 Spamhaus는 Data Query Service(DQS)를 안내합니다. 무엇이 무료인지는 해당 이용 조건을 확인하십시오. 실제로 어떤 리졸버를 사용하고 있는지 확인하려면 FAQ가 제안하는 dig txt whoami.fastly.net을 실행해 보십시오.
RFC 5782에는 기본적인 정상 동작 확인 장치도 있습니다. IPv4 리스트에는 127.0.0.2 항목이 있어야 하고 127.0.0.1 항목은 없어야 하며, 클라이언트는 리스트가 작동하는지 확인하기 위해 이 테스트 항목을 주기적으로 점검하는 것이 바람직합니다. Spamhaus의 예에서는 2.0.0.127.zen.spamhaus.org가 127.0.0.10, 127.0.0.4, 127.0.0.2를 반환하고, 1.0.0.127.zen.spamhaus.org는 NXDOMAIN을 반환합니다. 모니터는 이것을 먼저 테스트하고, 실패하면 그 회차의 결과를 버리는 것이 좋습니다.
방법 1: dig와 cron 스크립트
다음은 IPv4 주소 하나를 Spamhaus ZEN과 SpamCop(bl.spamcop.net — SpamCop 문서에 따르면 등재된 IP에는 127.0.0.2로 응답)에 대해 확인하는 최소한의 스크립트입니다. 주소와 존은 각자의 것으로 바꾸십시오.
#!/bin/sh
# 공개 리졸버가 아닌 자체 재귀 리졸버(예: 같은 호스트의 unbound)에 질의할 것
RESOLVER=127.0.0.1
IP=192.0.2.10
ZONES="zen.spamhaus.org bl.spamcop.net"
REV=$(echo "$IP" | awk -F. '{print $4"."$3"."$2"."$1}')
q() {
out=$(dig +time=3 +tries=1 @"$RESOLVER" "$1" A) || { echo ERR; return; }
case "$out" in
*"status: NOERROR"*) a=$(printf '%s\n' "$out" | awk '$4=="A"{print $5}'); echo "${a:-NX}" ;;
*"status: NXDOMAIN"*) echo NX ;;
*) echo ERR ;;
esac
}
for Z in $ZONES; do
# 127.0.0.2 테스트 항목이 등재 코드로 응답하지 않으면 이번 실행 결과는 신뢰하지 않음
case "$(q "2.0.0.127.$Z")" in
127.255.255.*|NX|ERR) echo "$Z ERROR test-entry"; continue ;;
127.*) ;;
*) echo "$Z ERROR test-entry"; continue ;;
esac
A=$(q "$REV.$Z")
case "$A" in
NX) echo "$Z CLEAN" ;;
ERR) echo "$Z ERROR no-answer" ;;
127.255.255.*) echo "$Z ERROR $A" ;;
127.*) echo "$Z LISTED $A" ;;
*) echo "$Z ERROR unexpected $A" ;;
esac
done스크립트가 하는 일:
- NXDOMAIN 상태만 깨끗한 것으로 간주하고, 127.255.255.x는 오류, 그 밖의 127.x는 등재로 처리합니다. 127.0.0.0/8 범위 밖의 응답은 응답을 다시 쓰는 리졸버 같은 이상 징후로 취급합니다.
- 타임아웃이 발생하면 dig는 실패 상태로 종료되지만, SERVFAIL과 REFUSED는 종료 코드 0으로 끝납니다. 어느 경우도 깨끗한 것으로 취급하지 않으며, 스크립트는 오류로 기록합니다.
- 반환 코드는 리스트마다 다릅니다. 예를 들어 127.0.0.1을 쿼리 거부의 의미로 쓰는 리스트도 있으므로, 추가하는 리스트마다 문서를 읽고 필요하면 case 문을 확장하십시오.
그런 다음 cron으로 매 실행 결과를 직전 결과와 비교해, 무언가 바뀌었을 때만 메일을 보내게 합니다.
# 예: 매시간 실행하고, 출력이 직전 실행과 다를 때만 메일 발송
0 * * * * /usr/local/bin/dnsbl-watch.sh > /var/tmp/dnsbl.now 2>&1; cmp -s /var/tmp/dnsbl.now /var/tmp/dnsbl.prev || mail -s "DNSBL status changed" you@example.com < /var/tmp/dnsbl.now; mv /var/tmp/dnsbl.now /var/tmp/dnsbl.prev매시간이라는 간격은 예시일 뿐입니다. Spamhaus는 소량의 비상업적 이용에 한해 무료로 제공하며, 쿼리량 기준을 넘는 클라이언트는 유료 옵션이 필요합니다. 모니터링하는 IP 수가 늘어나면 각 리스트의 이용 조건을 확인하십시오. FAQ는 결과가 일관되게 유지되는지 확인하기 위해 명령을 몇 차례 실행해 보라고도 조언합니다.
방법 2: DNSBL Checker 일괄 조회 API와 웹훅
리졸버와 리스트별 해석 규칙을 직접 관리하고 싶지 않다면 서비스에 확인을 맡길 수 있습니다. 아래는 이 사이트의 v1 API이며, 서버 구현이 실제로 하는 동작만 다룹니다. 레퍼런스는 /en/api-docs에 있고, 키는 /account의 회원 페이지에서 발급됩니다. 가입에는 이메일 주소만 필요하며, 이메일로 받은 링크로 로그인합니다.
인증과 한도
- 키는
dnsbl_로 시작하며, 발급 직후 한 번만 표시됩니다. 서버에는 해시만 저장됩니다. 키를 재발급하면 이전 키는 무효가 됩니다. - 모든 요청에서
X-API-Key헤더로 키를 보내십시오. HTTPS 전용이며, 일반 http:// 요청은 https:// 로 리디렉션되므로 처음부터 https:// 로 호출하십시오. - 한도는 하루 1,000건 조회(대상 하나당 한 건으로 계산), 배치당 대상 100개, 모니터 등록 50개입니다. 일일 한도를 넘으면
429와 함께daily_limit_exceeded와resets_at타임스탬프가 반환됩니다.
일괄 조회
도메인 이름이나 IP 주소로 이루어진 targets 배열을 POST /api/v1/batch로 보냅니다.
curl -X POST https://dnsblchecker.com/api/v1/batch \
-H "X-API-Key: $DNSBL_API_KEY" -H "Content-Type: application/json" \
-d '{"targets":["example.com"]}'아직 확인이 필요한 대상이 하나라도 있으면 응답은 202와 batch_id입니다. status가 done이 될 때까지 몇 초마다 GET /api/v1/batch/{batch_id}를 폴링하십시오(일부를 생략한 예시이며, 숫자와 리스트 이름은 설명용으로 실제 결과가 아닙니다).
{
"batch_id": 1, "status": "done", "created": "2026-11-03 09:10:41", "count": 1, "pending": 0,
"results": [
{
"target": "example.com", "type": "domain",
"status": "done", "verdict": "listed",
"checked_at": "2026-11-03 09:12",
"lists_checked": 80, "listed_count": 1, "error_count": 4,
"listed_on": [
{ "list": "ZapBL RHSBL", "zone": "rhsbl.zapbl.net", "response": "127.0.0.2" }
]
}
]
}verdict는 하나 이상의 리스트가 등재를 보고하면listed, 그렇지 않으면clean입니다. 타임아웃된 리스트나 127.255.255.x 같은 오류 코드로 응답한 리스트는error_count에 집계되며, 등재로 계산되지 않습니다.- 최근 30일 이내에 저장된 결과가 있으면 배치는 그 결과를 반환합니다. 정기 모니터링에서는 항상
checked_at을 확인하십시오. - 형식이 잘못된 대상은 이유와 함께
rejected로 반환되며 집계되지 않습니다. URL을 넘기면 호스트 이름만 사용됩니다. 192.0.2.0/24 같은 문서용 대역은 거부되므로 실제 발신 IP로 테스트하십시오.
웹훅 모니터링
POST /api/v1/monitors로 target과 callback_url을 등록합니다. 그러면 등록된 각 대상은 캐시를 쓰지 않고 하루에 한 번 확인되며(첫 확인은 등록 후 대략 하루 이내에 실행), 첫 확인 시(monitor.initial)와 판정이 listed와 clean 사이에서 바뀔 때(monitor.status_changed)에만 지정한 URL로 JSON POST가 전송됩니다.
curl -X POST https://dnsblchecker.com/api/v1/monitors \
-H "X-API-Key: $DNSBL_API_KEY" -H "Content-Type: application/json" \
-d '{"target":"example.com","callback_url":"https://your-app.example/hooks/dnsbl"}'- 응답에 포함된
signing_secret은 한 번만 표시됩니다. 각 전송에는X-DNSBL-Signature: sha256=...헤더가 붙으며, 이는 그 시크릿을 키로 원본 본문(raw body)을 계산한 HMAC-SHA256입니다. 수신 측에서 검증하십시오. callback_url은 443번 포트의 https여야 하며 공인 IP로 확인되어야 합니다. 리디렉션은 따라가지 않으며 타임아웃은 10초이므로, 무거운 작업은 큐에 넣고 빠르게 응답하십시오.- 엔드포인트가
2xx이외의 응답을 반환하거나 연결에 실패하면, 다음 일일 확인 때 다시 전송합니다. 실패 이유는GET /api/v1/monitors의last_delivery_error에 표시되며,DELETE /api/v1/monitors/{monitor_id}로 모니터를 삭제할 수 있습니다.
import hmac, hashlib
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers.get("X-DNSBL-Signature", ""))알림 설계: 빈도, 여러 리스트, 오탐
모니터는 몇 달이 지나도 사람들이 알림을 계속 읽어야 쓸모가 있습니다. 끊임없이 울리는 모니터는 곧 무시당합니다.
빈도
- 조회 빈도와 알림 빈도를 분리하십시오. 조회는 정해진 일정대로 하되, 알림은 상태가 바뀔 때만 보내십시오. 매일 오는 '이상 없음' 메시지는 정말 중요한 단 하나의 알림을 묻히게 만듭니다.
- RFC 5782는 리스트의 TTL이 리스트가 바뀌는 속도를 반영해야 하며, 스팸 발신원 리스트는 분 단위 TTL을 쓸 수 있다고 말합니다. 일부 등재는 빠르게 나타났다가 빠르게 만료된다는 점을 감안해 간격을 정하십시오.
- 과도하게 조회하면 리스트의 이용 조건을 위반하거나, 실제 응답 대신 127.255.255.255(과도한 쿼리)를 받을 수 있습니다.
여러 리스트
- 모든 알림에 리스트 이름을 넣으십시오. 리스트마다 사용하는 수신 측의 수가 다르므로, 수신자에게 가장 중요한 리스트는 즉시 알리고 나머지는 요약(다이제스트)으로 묶는 방법도 있습니다.
- 위의 웹훅은 판정이 listed와 clean 사이에서 바뀔 때만 발생합니다. 이미 등재된 대상이 두 번째 리스트에 추가로 오르는 경우에는 이벤트가 전송되지 않습니다. 이런 경우를 잡으려면
GET /api/v1/monitors의last_listed_count를 지켜보거나, 배치를 실행해listed_on을 확인하십시오.
오탐과 일시적인 응답
- 오류는 등재와 따로 기록하십시오. Spamhaus는 운영자에게 오류 코드를 근거로 메일을 거부하거나 필터링하지 말라고 경고합니다. 모니터링에도 같은 논리가 적용됩니다. 타임아웃이나 127.255.255.x 응답으로 등재 알림이 발생해서는 안 됩니다.
- 한 번의 이상한 결과에 반응하지 않도록, 같은 결과가 두 번 연속으로 나온 뒤에만 알리거나, 알림을 보내기 전에 몇 분 뒤 다시 조회하십시오.
- 오류가 계속되면 리졸버 변경이나 리스트 측의 변경을 의심하고, 먼저 테스트 항목의 결과를 확인하십시오.
- Spamhaus PBL에 오른 가정용 IP는 비난이 아니라 정책상의 등재로, 그 주소가 메일을 직접 보내서는 안 된다는 뜻입니다. 무엇을 알림으로 볼지는 리스트별로 정하십시오(Spamhaus 해제 참조).
등재가 감지되었을 때 할 일
- 확인합니다. 운영자의 공식 조회 페이지에서 확인하고, 리스트 이름, 시각, 표시된 이유를 기록하십시오.
- 원인부터 해결합니다. 탈취된 계정, 악용된 웹 양식, 감염된 기기, 부실한 수신자 목록 관리, 일치하지 않는 역방향 DNS(PTR)나 HELO 이름이 없는지 살펴보십시오. 원인이 남아 있는 상태에서 해제를 요청하면 대개 다시 등재됩니다.
- 해당 리스트의 해제 절차를 따릅니다. 해제 결정은 각 리스트 운영자에게 있으며, 요청을 제출했다고 해서 등재가 해제된다는 보장은 없습니다. 공유 호스팅처럼 IP를 직접 관리하지 않는다면 호스팅 제공업체나 ISP에 문의하십시오.
- 모니터로 결과를 확인합니다. 수신 측도 자체 필터를 운영하므로, 판정이 clean으로 돌아온 뒤에도 전달 상황을 지켜보십시오.
IP나 도메인을 한 번만 확인하려면 블랙리스트 검사 도구를 이용하십시오. 원인 추적이나 요청 처리를 감당하기 어렵다면 저희에게 도움을 요청하실 수 있습니다. 메일이 반송될 때의 전체 문제 해결 절차는 블랙리스트로 메일이 차단될 때를 참고하십시오.
블랙리스트 해제 신청 대행, 컨설팅, 자문(JPY 50,000부터)을 맡아 주는 업체를 소개해 드립니다. 해제 여부는 각 리스트 운영자가 결정하므로 해제를 약속할 수는 없습니다.
상담하기이 사이트는 소개한 업체로부터 소개 수수료를 받을 수 있습니다. About
자주 묻는 질문
8.8.8.8로 조회했더니 등재되지 않았다고 나옵니다. 깨끗한 것입니까?
반드시 그렇지는 않습니다. Spamhaus FAQ에 따르면 Google Public DNS 같은 무료 오픈 리졸버를 통한 쿼리는 대부분의 경우 등재되지 않음(NXDOMAIN) 응답을 받으므로, 등재된 IP가 깨끗해 보일 수 있습니다. 자체 리졸버에서 조회하거나 운영자의 조회 페이지를 이용하십시오.
127.255.255.254가 나왔습니다. 등재되었다는 뜻입니까?
아니요. 공개 리졸버나 오픈 리졸버를 통한 쿼리에 반환되는 오류 코드이며, Spamhaus는 이것이 대상이 등재되었다는 뜻이 아니라고 명시합니다. 오류로 기록하고 등재 알림은 보내지 마십시오.
배치 API를 매일 호출하는 것만으로 모니터링이 됩니까?
배치는 최대 30일 전의 저장된 결과를 반환할 수 있으므로, 항상 오늘의 상태라고는 할 수 없습니다. checked_at을 확인하거나, 매일 새로 확인하는 웹훅 모니터링(POST /api/v1/monitors)을 이용하십시오.
알림이 오면 바로 해제를 요청해야 합니까?
원인부터 해결하십시오. 문제가 남아 있는 상태에서 요청하면 대개 다시 등재되는 것으로 끝납니다. 결정은 언제나 리스트 운영자에게 있습니다.
참고한 공식 페이지
이 글의 사실 관계는 게시 시점에 아래 공식 페이지와 대조하여 확인했습니다. 정책과 동작은 예고 없이 바뀔 수 있으므로 중요한 판단을 내리기 전에 공식 페이지를 확인하시기 바랍니다.
- RFC 5782: DNS Blacklists and Whitelists
- Spamhaus FAQ: DNSBL Usage
- Spamhaus: Using our public mirrors? Check your return codes now
- SpamCop FAQ: How do I configure my mailserver to reject mail based on the blocklist?
관련 가이드
DNSBL 반환 코드 읽는 법: RFC 5782 기준 127.0.0.2의 의미, Spamhaus·SpamCop·UCEPROTECT의 코드, 오류 코드, NXDOMAIN과 SERVFAIL의 차이를 설명합니다.
Spamhaus에 등재되었나요? 어느 리스트(ZEN, PBL, XBL, CSS, SBL, DBL)인지 확인하는 법, 반환 코드의 의미, 리스트별 올바른 해제 절차를 설명합니다.
블랙리스트로 메일이 차단되었나요? 반송 코드를 읽고, 원인을 올바른 순서로 확인하고, 중요한 등재를 가려내 올바르게 해제를 요청하는 방법을 설명합니다.