1. 실행 환경

2. SPF 는 무엇인가요?

SPF는 Sender Policy Framework의 약자로, 메일서버 정보를 사전에 DNS에 공개 등록함으로써 수신자로 하여금 이메일에 표시된 발송자 정보가 실제 메일서버의 정보와 일치하는지를 확인할 수 있도록 하는 인증기술 입니다.

SPF 기술이 보편적으로 도입되기 전에는, 예를 들어 도메인의 소유자가 아닌 제 3자가 자신의 정보를 숨기기 위해, “xxxxx@example.com”을 “보낸 사람”으로 하여 스팸 메일을 발송하는 등의 일이 많았습니다.

이를 Email Spoofing (메일 스푸핑) 이라고 합니다.

SPF는 도메인 소유자가 설정한 정보를 토대로, 설정된 SMTP 서버에서 발송된 메일에만 SPF=PASS를 기록하게 하여 스푸핑된 메일이 아니라는 헤더를 만듭니다.

3. SPF 적용하는 방법

SPF의 적용은 매우 간단합니다.

발송하는 메일의 도메인에, SPF의 문법을 포함한 TXT 레코드를 입력하여 주시면 됩니다.

SPF 문법은 SMTP 서버 보유량, 도메인 관계 등에 따라 여러가지를 설정할 수 있지만, 우선 실제 예제를 보며 SMTP 서버를 인증하는 방법을 알아보겠습니다.

예제는 gmail.com의 레코드를 보며 설명 하겠습니다.

Gmail의 SPF 레코드는, 처음 보는 사람에게는 조금 복잡하게 느껴지실 수 있지만, 이는 실제 업무 환경에서의 적응에도 매우 좋은 사례라 판단되므로 (이유는 아래에 설명) gmail.com을 선택하였습니다.

먼저, dns 레코드 조회를 위해 dig 를 사용합니다.

$ dig -v

위의 명령어로 버전이 나오지 않는다면, dig를 설치해 줍니다.

$ sudo apt install dig -y

설치가 되면, gmail.com의 SPF 레코드를 조회해 보도록 하겠습니다.

$ dig gmail.com TXT

아래와 같은 결과가 나옵니다.

gmail.com. 299 IN TXT "v=spf1 redirect=_spf.google.com"

이를 해석해보자면,

라는 의미입니다.

gmail.com 자체의 SPF 레코드는, 특정 서버를 기술하지 않고 SPF Redirect를 사용하고 있습니다.

왜 그럴까요?

도메인의 TXT 레코드는 255자의 레코드 값 길이 제한이 있습니다. 만약, 적은 수의 SMTP 서버를 가지고 있다면 255자 안에서 설정을 끝낼 수 있지만, SMTP 서버로 사용중인 IP 주소만 수백, 수천개가 있다면, 255자 이내에 모든 서버를 적용할 수 없습니다. 그렇게 때문에, SPF 레코드를 2, 3단계 체인처럼 만들어 실제 적용되는 SMTP 서버의 주소를 찾아야 합니다.

조금 더 진행해 보겠습니다.

위에서 gmail.com 으로 발송되는 이메일에 대한 SPF 레코드는 _spf.google.com에서 찾도록 설정 되어 있으므로, 다음은 이를 조회해 보도록 하겠습니다.

$ dig _spf.google.com TXT

이번에는 아래와 같은 결과가 나왔습니다.

_spf.google.com. 277 IN TXT "v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all"

이를 해석해보자면,

include는 @gmail.com을 사용하여 보내는 SMTP 서버가 사용하는 실제 도메인 명을 포함한다는 의미입니다.

또한 include는, 자신의 SMTP 서버가 아닌, 외부 SMTP 서버를 이용하는 경우, 발신인을 자신의 도메인으로 하여도 위에서 설명한 “메일 스푸핑 처리하지 않겠다는 의미”입니다.

기업 환경에서, 대량 메일 발송을 위한 전문 업체, 혹은 서버 호스팅 업체의 SMTP 시스템을 사용하는 경우 등등에 많이 사용됩니다.

~all은 ~와 all을 나누어서 봅니다.

all은 특정 도메인 혹은 IP 주소를 서술한 후, 주로 가장 마지막에 적게되는데, 이는 서술된(SPF를 통과하는) 도메인 및 IP주소를 제외한 “나머지 모든 곳에서 발송된 @gmail.com을 발신자로 하는 이메일” 이라는 의미입니다.

~은, “all”에 대해 Softfail을 선언한다는 의미입니다.

아래에 설명되어 있지만, include 및 all은 메커니즘의 한 종류이며, ~은 수식자의 한 종류입니다.

Softfail은, “메일 수신자 측 SMTP 서버에 해당 메일에 대한 처리를 맡기겠다” 라는 의미입니다.

다시 진행해 보자면, include로 등록된 3개의 도메인 중, _netblocks.google.com의 SPF 레코드를 보겠습니다.

$ dig _netblocks.google.com TXT

이번에는 아래와 같은 결과가 나왔습니다.

_netblocks.google.com. 3599 IN TXT "v=spf1 ip4:64.18.0.0/20 ip4:64.233.160.0/19 ip4:66.102.0.0/20 ip4:66.249.80.0/20 ip4:72.14.192.0/18 ip4:74.125.0.0/16 ip4:108.177.8.0/21 ip4:173.194.0.0/16 ip4:207.126.144.0/20 ip4:209.85.128.0/17 ip4:216.58.192.0/19 ip4:216.239.32.0/19 ~all"

드디어 실제 IP 주소들이 나왔습니다.

SPF 레코드의 제일 앞의 IP 블럭 1개만 해석해 보자면,

첫 번째 _netblocks.google.com은 “서술된 IP 주소들을 SMTP 서버로 포함하여 사용중이겠구나” 하고 추측할 수 있습니다.

참고로, 두 번째 블럭도 검색해 보니, 여기에는 IPv6 주소들만 포함되어 있었습니다.

$ dig _netblocks2.google.com TXT

세 번째 블럭은, 172.217.0.0/19 만 포함하고 있네요.

dig _netblocks3.google.com TXT

gmail은 이처럼 SMTP 서버가 많기 때문에, 복잡한 체인 형태를 가지고 있습니다.

+ 보너스 +

자신의 도메인으로 메일을 발송하는 서버가 없는 경우, 메일 스푸핑 방지를 위해 어떻게 하면 좋을까요?

example.com의 TXT 레코드를 봅시다.

$ dig example.com TXT

example.com에 대한 TXT 레코드에 v=spf1 -all 가 있습니다.

허용하는 주소가 아무것도 없고, -all (-는 Hardfail 이라는 의미)로, 전 세계 어디에서 보낸 메일이든, @example.com을 발신인으로 사용 시 SPF=FAIL 헤더를 추가하게 됩니다.

물론 수신하는 측에서 SPF 필터를 사용하지 않는 다면 의미가 없겠지만요.

아래는 SPF 레코드의 문법에 관한 설명입니다.

4. SPF 레코드 문법

(출처 : “넷퍼씨메일링크” 블로그)

SPF는 SMTP 서버에서 따로 설정할 필요 없이, DNS 레코드만 입력해 주면 됩니다.

도메인은 0개 이상의 메커니즘(mechanisms)을 정의할 수 있습니다. 메커니즘이란 해당 도메인에서 발송서버로 지정된 호스트의 질의 형식이라고 생각하시면 됩니다.

all ip4 ip6 a mx ptr exists include 위와 같이 총 8개의 메커니즘이 존재합니다. 또한, redirect | exp 2개의 수식어도 쓸 수 있습니다.

메커니즘 앞에는 다음과 같은 4개의 수식자(qualifiers)가 붙을 수 있습니다. 기본값은 + 입니다. “+”Pass “-“Fail “~”SoftFail “?”Neutral

메커니즘은 순차적으로 평가되며 매치되는 항목이 없을 경우 기본 결과값은 Neutral 입니다.

어떤 도메인이 SPF 값을 DNS서버에 등록해 놓지 않은 경우 그 결과값은 None입니다. DNS처리 과정에서 일시적인 에러가 발생하였다면 결과값은 TempError입니다. 문법적으로 잘못된 SPF정보가 등록되었을 경우 결과값은 PermError입니다.

이 메커니즘은 항상 매치됩니다. 주로 SPF 레코드의 가장 마지막에 옵니다.

예)

“v=spf1 mx -all”

해당 도메인(SMTP 명령어중 MAIL FROM 에 사용되는 이메일 주소의 @ 뒷부분)의 MX서버를 질의 하여 발송서버의 IP가 MX서버의 IP와 일치하면 수신허용. 그렇지 않은 모든 경우는 수신거부

“v=spf1 -all”

해당 도메인은 어느 서버에서도 메일을 발송하지 않음

“v=spf1 +all”

해당 도메인은 SPF레코드가 쓸모없다고 생각하며 전혀 신경쓰지 않음.(어떤 IP에서 접속하든 수신허용하라는 뜻이므로)

ip4:

ip4:/

ip4메커니즘의 인자값은 IPv4의 네트워크 범위이다. 만약 prefix-length가 정의되지 않았다면 기본값은 /32이다.

예)

“v=spf1 ip4:192.168.0.1/16 -all”

192.168.0.1 와 192.168.255.255 사이의 IP주소만 pass하고 나머지는 fail

ip6:

ip6:/

“ip6:” mechanism 인자값은 IPv6 네트워크 범위이다. prefix-length가 정의되지 않았다면 기본값은 /128이다.

예)

“v=spf1 ip6:1080::8:800:200C:417A/96 -all”

1080::8:800:0000:0000 와 1080::8:800:FFFF:FFFF 사이의 IP주소만 pass, 나머지는 fail.

“v=spf1 ip6:1080::8:800:68.0.3.1/96 -all”

1080::8:800:0000:0000 와 1080::8:800:FFFF:FFFF사이의 IP주소만 pass, 나머지는 fail.

a

a/ a:a:/

해당도메인의 모든 A 레코드를 테스트한다. 발송서버의 IP가 그 중 하나라도 일치하면 pass

“v=spf1 a -all”

현재 도메인을 테스트한다.

“v=spf1 a:example.com -all”

현재 도메인이 example.com이라면 위와 동일하다.

“v=spf1 a:mailers.example.com -all”

mailers.example.com의 A레코드를 질의하여 나온 IP주소가 발송서버의 IP주소와 일치하면 통과

a메커니즘과 유사하게 DNS에 각각 A대신 MX와 PTR레코드를 질의하여 그 결과값으로 판단한다.

ptr

ptr:

발신서버의 IP주소에 대한 PTR 질의를 통하여 호스트명을 찾아낸 후 호스트명을 가지고 판단한다. 찾아낸 호스트의 A레코드 중 최소 하나이상이 발신서버의 IP와 일치하여야 한다. 도메인 명이 명시되지 않았다면 현재 도메인이 사용된다. 가능하다면 이 메커니즘은 사용을 피해야 하는데 과도한 DNS질의를 수행하게 되기 때문이다.

예)

“v=spf1 ptr -all”

모든 서버를 직접 관리하는 도메인의 경우 이 모든 서버들로 부터 메일이 발송되는 것을 허용할 수 있다. 예를 들어 hotmail.com이나 paypal.com은 이와 같은 SPF를 정의할 수도 있다.

“v=spf1 ptr:otherdomain.com -all”

도메인 명이 otherdomain.com으로 끝나는 모든 호스트명을 지정.

주어진 도메인에 대한 A 타입 질의를 수행하여 결과가 나온다면 통과.

결과로 나온 IP주소가 어떠한 것이든 상관없다.

예)

다음 예에서 발신서버의 IP주소는 1.2.3.4 이고 현재 도메인은 example.com이다.

“v=spf1 exists:example.com -all”

만약 example.com 에 대한 A질의가 실패한다면 결과도 실패이다. 질의결과가 성공이면(A레코드가 발견되면) 통과이다.

include:

주어진 도메인에 대한 SPF질의를 수행한다. 만약 해당 도메인이 유효한 SPF정보를 가지고 있지 않다면 결과는 permanent error이다.

예)

다음 예에서 발신서버의 IP주소는 1.2.3.4 이고 현재 도메인은 example.com이다.

“v=spf1 include:example.com -all”

example.com 이 SPF 정보를 가지고 있지 않다면 결과는 PermError이다.

example.com의 SPF record정보가 “v=spf1 a -all” 인 경우를 가정해보자.

examplecom의 A타입 질의를 수행한 결과가 1.2.3.4 라면 통과이다.

만약 매치되는 결과가 없다면 include는 실패이고 이 예에서의 전체적인 결과도 실패이다.

신뢰관계 - include 메커니즘은 도메인 간의 상호 관리 범위를 의미한다. include를 사용할 경우에는 악의적 사용자에게 이용되지 않도록 주의하여야 한다. 정의된 외부 도메인에 악의적 사용을 제한할 수 있는 기술적 장치가 없다면 include메커니즘은 Pass보다는 Neutral 결과를 주어야 한다. include 앞에 “?”를 붙임으로써 이러한 방법이 가능하며 이러한 모양이 될 것이다.

“v=spf1 ?include:example.com -all”

SPF 는 간단하면서도, 설명할 내용이 제법 많았네요. 자신도 모르는 사이에 누군가가 자신의 도메인 명으로 메일 스푸핑을 하여, 다른 SMTP 서버에서 영구블럭 당하기 전에 SPF 레코드를 추가해 보시는건 어떨까요? :)