SPF, DKIM, DMARC는 각각 무슨 일을 하나
레코드 셋, 역할 셋. 각각이 무엇을 증명하는지, DMARC가 왜 나머지 둘을 필요로 하는지, 어떤 순서로 적용해야 메일이 끊기지 않는지 정리했습니다.
레코드 셋, 역할도 셋
세 가지는 늘 한 묶음으로 언급되지만, 사실 서로 완전히 다른 질문에 답합니다. 이 구분을 정확히 잡는 것이 거의 전부입니다.
| 레코드 | 답하는 질문 | 전달돼도 유지되나? |
|---|---|---|
| SPF | 이 메일이 도메인이 허가한 서버에서 발송됐는가? | 아니요 |
| DKIM | 이 메일이 그 도메인이 서명했고 이후 변조되지 않았는가? | 대체로 유지됨 |
| DMARC | 앞의 둘이 실패했을 때 수신 서버가 어떻게 하고, 어디로 보고할 것인가? | 해당 없음 |
SPF는 봉투 발신자를 IP 목록과 대조합니다. DKIM은 암호학적 서명을 검증합니다. DMARC는 그중 통과한 쪽이 수신자가 From 헤더에서 보는 도메인과 같은지 확인하고, 그에 따라 수신 서버가 무엇을 할지 지시합니다.
SPF: 어떤 서버가 보낼 수 있는가
SPF는 도메인 최상위에 두는 TXT 레코드 하나로, 그 도메인의 메일을 보낼 수 있는 발신원을 나열합니다. 가장 단순한 형태는 이렇습니다.
v=spf1 include:_spf.google.com ~all
왼쪽부터 읽으면 SPF 버전 1을 쓰고, 구글이 게시한 목록에 있는 것은 허가된 것으로 보며, 그 밖의 모든 것은 소프트 실패로 처리한다는 뜻입니다.
마지막 메커니즘은 의식적으로 골라야 하는 부분입니다.
-all(실패) — 목록에 없는 것은 거부해야 합니다. 가장 강한 설정이며, 최종적으로 도달할 목표입니다.~all(소프트 실패) — 받되 의심스러운 것으로 표시합니다. 아직 발신원을 파악하는 중이라면 이 설정이 맞습니다.?all(중립) — 아무 말도 하지 않는 것이라 레코드가 없는 것과 큰 차이가 없습니다.+all— 인터넷 전체를 허가합니다. 올바른 경우가 없습니다.
SPF의 근본적인 약점은 전달입니다. 메일링 리스트나 전달 주소가 메일을 중계하면 수신 서버에는 전달자의 IP가 보이는데, 그 IP는 내 레코드에 없으므로 내 잘못이 아닌데도 SPF가 실패합니다. 바로 이 빈틈을 DKIM이 메웁니다.
DKIM: 메일이 변조되지 않았다는 증명
DKIM은 보내는 메일마다 본문과 지정한 헤더들에 대한 암호학적 서명을 붙입니다. 수신 서버는 DNS에서 공개키를 가져와 그 서명을 검증합니다.
키는 selector1._domainkey.example.com처럼 셀렉터가 붙은 이름에 둡니다. 한 도메인이 동시에 여러 키를 쓸 수 있도록 한 설계이며, 키 교체가 가능한 이유이기도 합니다. 그래서 업체가 쓰는 셀렉터를 모르면 DKIM을 확인할 수 없습니다.
서명이 메일과 함께 이동하기 때문에 DKIM은 보통 전달을 거쳐도 유지됩니다. 다만 전달 도중 서명 대상이 바뀌면 깨집니다. 메일링 리스트가 하단 문구를 덧붙이거나 제목을 고쳐 쓰는 경우가 대표적입니다.
정렬(alignment): 셋을 하나로 묶는 조건
DMARC를 단순한 껍데기 이상으로 만드는 개념이자, 대부분의 설명이 건너뛰는 부분입니다.
메일에는 "보낸 사람" 주소가 둘 있습니다. SPF가 검사하는 것은 봉투 발신자(Return-Path)이고, 수신자가 실제로 보는 것은 From 헤더입니다. 둘이 같을 필요가 없다는 점이 오랫동안 문제의 핵심이었습니다. 공격자가 자기 도메인으로 SPF를 통과시키면서 화면에 보이는 From에는 남의 도메인을 넣을 수 있었기 때문입니다.
DMARC는 정렬을 요구해 이 구멍을 막습니다. SPF나 DKIM을 통과한 도메인이 From 헤더의 도메인과 일치해야 합니다.
- 완화(relaxed) 정렬(기본값) — 조직 도메인이 같으면 됩니다. 그래서
mail.example.com은example.com과 정렬됩니다. - 엄격(strict) 정렬 — 도메인이 정확히 같아야 합니다.
SPF는 통과했는데 DMARC는 실패하는 상황이 생기는 이유가 여기 있습니다. 디버깅할 때 가장 헷갈리는 결과인데, SPF 검사는 성공했지만 From 헤더와 다른 도메인에 대해 성공한 것입니다.
DMARC: 검사에 실패했을 때 무엇을 할지
DMARC는 _dmarc 서브도메인에 두는 TXT 레코드입니다.
v=DMARC1; p=none; rua=mailto:[email protected]; pct=100; adkim=r; aspf=r
중요한 태그는 다음과 같습니다.
| 태그 | 의미 |
|---|---|
| p | 실패 시 정책: none(관찰), quarantine(스팸함), reject(거부) |
| rua | 집계 보고서를 받을 주소. 이것이 없으면 DMARC는 아무것도 알려 주지 않습니다. |
| pct | 정책을 적용할 실패 메일의 비율. 점진적 적용을 위한 손잡이입니다. |
| sp | 서브도메인 정책. 기본값은 p이며, 서브도메인이 다르면 명시하세요. |
| adkim / aspf | 정렬 모드. r(완화, 기본) 또는 s(엄격) |
p=none인데 rua가 없는 레코드는 최악입니다. DMARC가 있는 것처럼 보이지만 아무것도 적용하지 않고 아무것도 알려 주지 않습니다. 오늘 하나만 게시한다면 반드시 보고 주소가 함께 있는 p=none을 게시하세요.
메일을 잃지 않는 적용 순서
순서가 중요합니다. 발신원 목록을 파악하기 전에 정책을 적용하면 정상 메일이 조용히 사라지기 시작합니다.
- 내 도메인으로 보내는 것들을 먼저 목록화합니다. 마케팅 플랫폼, 고객센터, 청구 시스템, CI 알림, 몇 해 전 누군가 붙여 둔 CRM까지. 이 목록은 언제나 예상보다 깁니다.
~all로 SPF를 게시합니다. 찾아낸 모든 발신원을 포함하되, 이 단계에서는 하드 실패가 아니라 소프트 실패로 둡니다.- DKIM 서명을 켭니다. 지원하는 모든 플랫폼에서 켜고 각 키를 게시합니다.
p=none과rua주소로 DMARC를 게시합니다. 아직 적용되는 것은 없고 보고서가 들어오기 시작합니다.- 2~4주 동안 보고서를 읽습니다. 실패하는 발신원이 드러나는데, 그중 일부는 빠뜨린 정상 시스템입니다. 정책을 적용하기 전에 먼저 고치세요.
p=quarantine으로 옮깁니다. 필요하면pct=10으로 시작해 문제가 없으면 비율을 올립니다.p=reject로 옮깁니다. quarantine에서 몇 주 동안 문제가 없었을 때입니다.- 마지막으로 SPF를
-all로 조입니다. 발신원 목록이 완전하다고 확신할 때 하세요.
p=reject 전까지는 모든 단계를 되돌릴 수 있습니다. 빨리 가겠다고 보고 단계를 건너뛰지 마세요. 이 과정이 안전한 이유가 바로 그 단계에 있습니다.
실제로 메일을 끊어 먹는 실수들
- SPF 레코드 두 개. 기존 레코드에
include를 합치는 대신 업체가 알려 준 레코드를 그대로 추가한 경우입니다. PermError이고 전체가 실패합니다. - 조회 10회 제한 초과. 업체 네다섯 곳이 각자 include를 중첩하면 금방 넘습니다. 가능한 것은 펼쳐서 정리하고, 더는 쓰지 않는 발신원은 지우세요.
- DMARC를 엉뚱한 이름에 게시. 최상위가 아니라
_dmarc.example.com에 있어야 합니다. - 바로
p=reject로 직행. 목록 없이 적용하면 내가 보낸 청구서가 조용히 거부됩니다. - 서브도메인을 잊는 것.
sp가 없으면 서브도메인은p를 물려받습니다. 대개 그게 맞지만, 가정하지 말고 확인하세요. - SPF에서
ptr사용. 폐기된 데다 느리고 불안정합니다. 지우세요. rua주소 없음. 아무것도 보지 못한 채 운영하게 되고, 발신원이 깨져도 누군가 항의할 때까지 모릅니다.
자주 묻는 질문
셋 다 필요한가요, SPF만으로는 안 되나요?
SPF의 조회 10회 제한이 무엇인가요?
include, a, mx, ptr, exists, redirect가 각각 세어지고 include는 중첩되기 때문에, 업체 하나의 include가 혼자 여러 번을 잡아먹기도 합니다. 넘으면 PermError이고, 많은 수신 서버가 이를 무시하지 않고 SPF 실패로 처리합니다.SPF 레코드를 두 개 둬도 되나요?
v=spf1로 시작하는 TXT 레코드를 정확히 하나만 게시해야 합니다. 두 개면 PermError이며, SPF 설정 실수 중 가장 흔한 경우입니다. 대개 기존 레코드에 include를 합치는 대신 새 업체의 레코드를 그대로 추가해서 생깁니다.DKIM 레코드는 어디에 두나요?
<셀렉터>._domainkey.<도메인>에 둡니다. 셀렉터는 메일에 서명하는 쪽이 정하므로 업체에서 받아야 합니다. DNS만으로는 알아낼 방법이 없어서 DKIM 검사를 하려면 셀렉터를 직접 입력해야 합니다.p=none은 의미가 없나요?
p=none에서는 아무것도 거부되지 않지만, 내 도메인 이름으로 메일을 보내는 모든 발신원(잊고 있던 것들까지)이 담긴 집계 보고서를 받기 시작합니다. 그 목록이 있어야 나중에 quarantine으로 안전하게 넘어갈 수 있습니다.내 도메인에서 직접 확인하기
위 내용은 지금 내 설정이 어떤 상태인지 볼 수 있어야 쓸모가 있습니다. 아래 도구로 바로 확인해 보세요.
이 가이드에서 잘못된 내용을 발견하셨나요? 다음 주소로 알려 주세요: [email protected] — 확인해서 고치고, 위의 수정일도 함께 갱신합니다.