ETC

Email DKIM(DomainKeys Identified Mail) 메시지 서명 및 검증 프로세스

알쓸개잡 2023. 11. 8.

DomainKeys Identified Mail (이하 DKIM)은 공개키 암호화 및 키 서버 기술을 사용하는 이메일에 대한 도메인 수준 인증 프레임워크를 정의하여 MTA(메일 전송 에이전트) 또는 MUA(메일 사용자 에이전트)에서 메시지의 출처와 내용을 확인할 수 있도록 하는 송신자 인증 기술 중 하나다.

DKIM의 궁극적인 목표는 메시지 서명자의 신원과 메시지의 무결성을 보호하는 것이다.

메시지 수신자는 서명자의 도메인을 직접 쿼리하여 검색된 공개키를 기반으로 서명을 확인하고 도메인 개인키를 소유한 소유자가 메시지를 증명했음을 확인할 수 있다.

DKIM 서명 검증에 사용되는 공개키는 DNS 레코드에 등록되므로 DNS 관리 및 DNS 시스템 보안에 의존성이 있다.

 

DKIM 동작 방식

  • 메시지 전송시 개인키와 해시 알고리즘을 사용하여 메시지에 대한 서명을 생성 후 헤더에 추가하여 메시지를 전송한다.
  • 메시지 수신시 DNS를 통해 조회된 공개키와 해시 알고리즘을 사용하여 메시지 서명을 검증한다.

DKIM 동작 프로세스
메시지 서명 및 검증

 

DKIM 프로토콜 주요 요소

selector

  • 서명 도메인당 여러 개의 동시 공개키를 지원하기 위해서 selector를 사용하여 세분화한다.
  • 서명 도메인의 공개키를 DNS 조회하기 위해서는 selector 정보를 알아야 한다.
<selector>._domainKey.<domainName>

ex) 다음은 jetbrain.com 도메인에 대한 DKIM 공개키 조회 예이다.
txt 레코드 조회
set type=txt
ns1._domainKey.jetbrain.com
ns1 은 jetbrain.com 도메인의 selector 가 된다.
ns1 은 추후에 설명할 메시지 서명 헤더인 DKIM-Signature 헤더의 태그 정보를 통해서 확인할 수 있다.

 

각 도메인의 공개키 및 해당 selector의 수는 도메인 소유자가 결정한다.

대부분 도메인 소유자는 하나의 selector로 충분하지만, 관리적으로 분산된 조직은 다른 지역 또는 다른 이메일 서버에서 서로 다른 selector와 키 쌍을 관리하도록 선택할 수 있다.

 

태그목록

  • DKIM은 DKIM-Signature 헤더 및 도메인 서명 레코드에 간단한 tag=value 문법을 사용한다.
  • 세미콜론(';') 문자는 각 태그를 구분하므로 태그 값에 세미콜론 문자를 포함하면 안 된다.

 

서명 및 검증 해시 알고리즘

  • 메시지를 서명 및 검증하기 위해서 지원되는 해시 알고리즘은 rsa-sha1, rsa-sha256이다.
  • 알고리즘이 지정되지 않은 경우 기본값은 rsa-sha256이다.
  • 상대적으로 보안 수준이 높은 rsa-sha26을 사용하도록 권장한다.

 

Key Size

서명에 사용되는 Key Size는 최소 1024bit 이상의 RSA 키를 사용해야 한다.

키 크기 선택에 영향을 미치는 요소는 다음과 같다.

  • 4096bit 와 같은 큰 키는 512 바이트 DNS UDP 응답 패킷에 맞지 않을 수 있다.
  • 1024bit 보다 작은 키는 오프라인 공격의 대상이 될 수 있다.
  • key size가 클수록 이메일 확인 및 서명을 위해 더 높은 CPU 비용이 발생한다.
  • key는 정기적으로 교체할 수 있으므로 수명이 상대적으로 짧을 수 있다.

 

Canonicalization (정규화)

메시지 헤더와 본문에 대한 서명을 진행할 때 헤더와 본문의 데이터를 정규화된 규칙대로 일관되게 변경하여 서명을 하도록 하는데 이를 Canonicalization (정규화)라고 한다.

정규화는 메시지 서명 및 검증을 위해서만 사용되며 전송된 이메일에는 어떠한 영향을 미치지 않고 미쳐서도 안된다.

정규화 방식에는 simple과 relaxed 방식이 있다.

 

simple 헤더 정규화 알고리즘

  • 헤더 필드를 어떤 방식으로든 변경하지 않는다.
  • 헤더 필드 이름은 대소문자를 구분하지 않아야 하며 공백을 변경하지 않아야 한다.

 

relaxed 헤더 정규화 알고리즘

다음 단계를 순서대로 적용해야 한다.

  • 모든 헤더 필드 이름(헤더 필드 값은 제외)을 소문자로 변환한다. (ex. SUBject: AbC -> subject: AbC)
  • 여러 라인으로 구성된 헤더의 경우 CRLF 뒤에 WSP 문자(tabl 혹은 space)가 오는 경우 CRLF는 제거한다.
  • 헤더 필드 값의 마지막 CRLF는 제거하지 않는다.
  • 하나 이상의 연속된 WSP 문자는 단일 SP(space) 문자로 변환한다.
  • 헤더 필드 끝에 있는 모든 WSP 문자를 제거한다.
  • 헤더 필드 이름과 헤더 필드 값을 구분하는 콜론(':') 문자 앞뒤에 있는 모든 WSP 문자를 제거한다.

 

simple 본문 정규화 알고리즘

  • 메시지 본문 끝의 모든 빈 줄을 무시한다.
  • 메시지 본문에 본문이 없거나 후행 CRLF가 없는 경우 CRLF가 추가된다.
    • 완전히 비어있거나 누락된 본문은 단일 CRLF로 정규화한다.

 

relaxed 본문 정규화 알고리즘

  • 줄 끝의 모든 공백을 무시한다. 각 줄 끝에 있는 CRLF는 제거하지 않는다.
  • 한 줄 내의 모든 연속된 WSP 문자는 단일 SP(space) 문자로 줄인다.
  • 메시지 본문 끝의 모든 빈 줄을 무시한다.
relaxed 본문 정규화 알고리즘은 단어 사이의 간격을 조정하여 메시지를 전달할 수 있는 매우 조잡한 "ASCII Art" 공격의 특정 유형을 허용할 수 있다. 이러한 공격이 우려되는 경우 simple 본문 정규화 알고리즘을 대신 사용하는 것이 좋다.

 

 

정규화 예시

다음과 같은 메시지를 예를 들었을 때 각 정규화 결과를 살펴보면 다음과 같다.

A:<SP>X<CRLF>
B<SP>:<SP>Y<HTAB><CRLF>
<HTAB>Z<SP><SP><CRLF>
<CRLF>
<SP>C<SP><CRLF>
D<SP><HTAB><SP>E<CRLF>
<CRLF>
<CRLF>

 

헤더와 본문 모두 relaxed 정규화를 사용하는 경우

헤더는 다음과 같이 정규화한다.

a:X<CRLF>
b:Y<SP>Z<CRLF>

 

분문은 다음과 같이 정규화한다.

<SP>C<CRLF>
D<SP>E<CRLF>

 

헤더와 본문 모두 simple 정규화를 사용하는 경우

헤더는 다음과 같이 정규화한다.

A:<SP>X<CRLF>
B<SP>:<SP>Y<HTAB><CRLF>
<HTAB>Z<SP><SP><CRLF>

 

본문은 다음과 같이 정규화한다.

<SP>C<SP><CRLF>
D<SP><HTAB><SP>E<CRLF>

 

 

DKIM-Signature 헤더

  • 이메일의 서명 정보는 DKIM-Signature 헤더 필드에 저장된다.
  • DKIM-Signature 헤더 필드에는 모든 서명 및 공개키를 가져오기 위한 정보들이 포함되어 있다.
  • DKIM-Signature 헤더 값은 태그 목록으로 구성된다. (tag=value; tag=value;... 형식)
  • 서명을 위해 지정된 헤더 필드가 서명된 후에 생성 중인 DKIM-Signature 헤더 필드는 항상 서명 계산에 암묵적으로 추가된다.
  • 이때 DKIM-Signature 헤더 필드의 b= 태그에는 빈 문자열로 세팅하여 서명한다.

예를 들어 생성된 DKIM-Signature 헤더가 다음과 같을 때

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; 
    t=1680846077; i=@test.com; bh=AC7JjovhH77RvEXqOtcReF5+pBjnu7glVCsWM5ALEID=; 
    h=Date:From:To:Message-ID:Subject:Content-Type:Cc; 
    b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR

b= 태그에 계산된 서명 데이터는 

h= 태그에 지정된 Date, From, To, Message-ID, Subject, Content-Type, Cc 헤더 필드들과 

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; 
    t=1680846077; i=@test.com; bh=AC7JjovhH77RvEXqOtcReF5+pBjnu7glVCsWM5ALEID=; 
    h=Date:From:To:Message-ID:Subject:Content-Type:Cc; 
    b=<빈문자열>

위와 같이 b= 태그가 빈 문자열로 세팅되고 다른 모든 태그들이 세팅된 현재 생성 중인 DKIM-Signature 헤더를 더하여 c= 태그에 지정된 헤더 정규화 알고리즘으로 정규화를 수행 후에 개인키와 a= 태그에 지정된 해시 알고리즘을 사용하여 만들어진 데이터다.

 

DKIM-Signature 헤더 태그

DKIM-Signature 헤더의 각 태그는 다음과 같다.

Required는 필수 항목이고 Optional은 생략 가능한 항목이지만 default 값으로 동작해야 한다. Recommended는 권장사항이다.

v= Required 버전 (현재 v=1로 고정)
a= Required 서명 알고리즘 (rsa-sha1 혹은 ras-sha256)
b= Required 서명 데이터 (base64), 이 값에서 공백은 무시된다. 원본 서명을 재조립할 때 공백은 무시해야 한다. (\t, \r, \n, space 와 같은)
bh= Required 정규화된 메시지 본문 부분의 해시(base64). 이 값에서 공백은 무시된다. (\t, \r, \n, space 와 같은)
c= Optional 메시지 정규화 방식 (default는 simple/simple 이다). 헤더정규화방식/본문정규화방식 순서로 정의된다.
d= Required 서명 도메인. 공개키에 대해서 쿼리를 수행할 도메인이다. 이 도메인은 i= 태그의 상위 도메인과 같거나 동일해야 한다. 조건이 충족되지 않으면 검증자는 해당 서명을 유효하지 않은 것으로 간주해야 한다.
h= Required 서명 대상 헤더 필드. 서명 대상 헤더 필드를 식별하는 콜론(:)으로 구분된 헤더 필드 이름 목록이다. 이 필드에는 서명 알고리즘에 제시된 순서대로 헤더 필드의 전체 목록이 포함되어야 한다. 콜론 구분 기호 양쪽에 FWS(folding white space)가 포함될 수 있다. 헤더 필드 이름은 대소문자를 구분하지 않고 실제 헤더 필드 이름과 비교해야 한다.
i= Optional 메시지를 대신하여 서명하는 사용자. 기본값은 @<d태그도메인>. 도메인 부분은 d= 태그의 값과 동일하거나 하위 도메인이어야 함. 형식은 [local-part]@domain-name
l= Optional 본문 길이 제한 수. default는 본문 전체.
q= Optional 공개키를 검색하는 데 사용되는 쿼리 방법. default는 “dns/txt”.
s= Required d= 태그의 네임스페이스를 세분화하는 selector. (쿼리는 <selector>._domainKey.<domain>)
t= Recommended 서명된 시간(초단위 타임스탬프).
x= Recommended 서명 만료 시간(초단위 타임스탬프). 형식은 t= 태그와 동일. 검증자의 검증시간이 만료 시간을 지난 경우 서명은 유효하지 않은 것으로 간주될 수 있음. 검증 시간은 신뢰할 수 있는 시간인 경우 검증자의 관리 도메인에서 메시지가 처음 수신된 시간이어야 하며, 그렇지 않은 경우 현재 시간을 사용해야 함. x= 태그의 값은 t= 태그의 값보다 커야 함.
z= Optional 복사된 헤더 필드. 필드 이름과 값을 포함하여 메시지가 서명 될 때 존재했던 선택된 헤더 필드의 ‘|’ 문자로 구분된 목록. 서명 시점에 존재하는 모든 헤더 필드를 포함할 필요는 없음. h= 태그에 나열된 헤더 필드와 동일한 헤더 필드가 포함될 필요는 없음.

 

 

DKIM TXT 레코드 태그

DKIM 검증을 위한 공개키 조회를 위한 매개 변수는 조회 유형(q= 태그), 서명자 도메인(d= 태그), selector (s= 태그)이다.

공개키 조회를 위한 DNS 쿼리는 다음과 같다.

q=dns/txt 인 경우
<selector>._domainKey.<domain> 형식으로 txt 레코드를 조회한다.

 

레코드의 각 필드 정의는 다음과 같다.

v= Recommended DKIM key 레코드 버전 (v=DKIM1 고정)
g= Optional DKIM-SIgnature 헤더 필드의 i= 태그의 로컬 부분(i= 태그가 지정되지 않은 경우 빈 문자열)과 일치해야 한다. user+*, *-offer 와 같은 와일드카드를 허용한다. 이 태그의 목적은 특별한 용도로만 사용해야 하는 키를 제3자에게 위임할 때 이 selector를 합법적으로 사용할 수 있는 서명 주소를 제한하는 것이다.
h= Optional 허용되는 해시 알고리즘. 사용할 수 있는 해시 알고리즘을 ‘:’ 문자로 구분한 목록. 디폴트는 모든 알고리즘 허용. 서명자와 검증자는 반드시 sha256 해시 알고리즘을 지원해야 한다. (sha1:sha256)
k= Optional key type. 디폴트는 “rsa”. 서명자와 검증자는 반드시 rsa 키 타입을 지원해야 한다.
n= Optional 추가정보(메모/코멘트) 디폴트는 빈 값. 최종 사용자가 아닌 관리자가 사용하기 위한 용도.
p= Required base64로 인코딩된 공개키 데이터. 값이 비어있는 경우 이 공개키가 해지 되었음을 의미함. 이 태그 값의 구문과 의미는 k= 태그에 의해 정의됨. (RSA)
s= Optional ‘:’ 문자로 구분된 서비스 타입 목록. 디폴트는 “*”. * : 모든 서비스 유형과 일치 email : 전자 메일 (SMTP로 제한되지 않음) 이 태그는 향후 다른 서비스에서 DKIM 사용을 정의할 경우 다른 용도로 키를 사용하는 것을 제한하기 위한 장치.
t= Optional ‘:’ 문자로 구분된 플래그 목록. 정의된 플래그는 다음과 같다. y : 이 도메인은 DKIM을 테스트 중임을 나타내는 플래그. 이 경우 검증자는 서명되지 않은 다른 메일과 다르게 취급해서는 안된다. s : DKIM-Signature 헤더의 i= 태그가 지정된 경우 i= 태그의 ‘@’ 오른쪽에 있는 도메인 값과 d= 태그의 값이 동일해야 함을 나타내는 플래그. i= 태그의 도메인은 d= 태그의 하위 도메인이 아닌 일치해야 함. 하위 도메인이 필요한 경우가 아니라면 이 플래그를 사용하는 것이 좋다고 함.

 

 

서명에 포함할 헤더 필드 결정

DKIM-Signature의 h= 태그에는 서명에 포함할 헤더 필드 이름 목록을 지정해야 하는데 다음 사항에 유의해야 한다.

  • From 헤더는 반드시 서명에 추가되어야 한다.
  • 메일 전송 중에 합법적으로 수정되거나 제거될 수 있는 헤더 필드는 서명에 추가하지 않는다.

서명할 헤더 필드를 선택하는 것은 명확하게 정의된 것은 없지만 몇 가지 선택 전략이 있다.

  1. 기존의 반복되지 않는 모든 헤더 필드에 대해서 서명하는 전략
  2. 수신자의 메시지 처리에 영향을 미칠 가능성이 있는 헤더 필드에만 서명하는 전략
  3. 잘 알려진 헤더 필드에 대해서만 서명하는 전략

검증자는 서명되지 않은 헤더 필드를 사용자에게 표시하지 않거나 특정 헤더 필드를 서명에 포함하지 않는 경우 서명을 무시하는 등의 처리를 할 수 있기 때문에 Date, Subject, To, Sender, Reply-To 및 모든 MIME 헤더 필드와 같이 메시지에 존재하는 필드에 서명하는 것을 권고하고 있다.

 

Received 헤더와 같이 메시지에 두 번 이상 반복되는 헤더 필드를 서명에 추가하는 경우 헤더 블록에서 물리적으로 마지막 위치에 있는 헤더에 대해서 서명을 해야 한다. (가장 처음 생성된 헤더를 의미한다.)

반복되는 헤더 필드 중에 여러 인스턴스 헤더를 서명하려는 경우 h= 태그에 헤더 필드 이름을 여러 번 포함해야 하고 메시지 헤더 블록의 아래에서 위 방향 순서로 서명해야 한다.

예를 들어

Received: <A>
Received: <B>
Received: <C>

와 같이 반복되는 Received 헤더가 있고 h= 태그에 다음과 같이 지정된 경우

DKIM-Signature: ... h=Received:Received: ...
Received: <C>
Received: <B>

와 같은 순서로 Received 헤더 필드가 서명에 추가된다.

 

서명자는 해당 이름의 헤더 필드를 추가해서는 안된다는 것을 나타내기 위해 실제 해당 헤더 필드 보다 더 많은 헤더 필드 이름을 h= 태그에 포함할 수 있다.

무슨 말이냐면, 예를 들어 만약 h= 태그가 다음과 같이 지정된 경우

DKIM-Signature: ... h=Received:Received:Received:Received: ...
Received: <C>
Received: <B>
Received: <A>
Received:<빈문자열>

와 같은 순서로 Received 헤더가 서명될 텐데 수신자 측에서 추가적인 Received 헤더를 붙여서 메일이 다른 서버로 전송이 되는 경우

Received: <C>
Received: <B>
Received: <A>
Received: <수신자가 붙인 값>

으로 변경되면서 수신 측에서 DKIM 검증에 실패하게 되어 추가 헤더를 붙이지 못하도록 하는 효과를 의미하는 듯한다.

개인적으로 실제 테스트 검증은 해보지는 않았다. 위와 같이 동작할 것 같다로 개인적인 이해로 봐주면 좋겠다.

 

권장 서명 헤더

  • From (필수로 포함되어야 함)
  • Sender, Reply-To
  • Subject
  • Date, Message-ID
  • To, Cc
  • MIME-Version
  • Content-Type, Content-Transfer-Encoding, Content-ID, Content-Description
  • Resent-Date, Resent-From, Resent-Sender, Resent-To, Resent-Cc, Resent-Message-ID
  • In-Reply-To, References
  • List-Id, List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive

서명에 포함되지 않아야 할 헤더

  • Return-Path
  • Received
  • Comments, Keywords
  • Bcc, Resent-Bcc

 

DKIM-Signature 헤더 생성

DKIM-Signature 헤더를 생성하는 목적은 결국 두 개의 서명 데이터를 생성하여 세팅하는데 목적이 있다.

  • 메시지 본문 해시 (bh= 태그)
  • 메시지 헤더 해시 및 서명 (b= 태그)

DKIM-Signature 헤더를 생성하기 전에 메시지 헤더 해시를 서명하기 위한 개인키가 필요하다.

메일 수신 측에서는 DKIM-Signature 헤더를 검증해야 하기 때문에 해당 도메인에 DKIM 검증을 위한 공개키가 DNS에 TXT 레코드로 등록되어 있어야 한다.

ex)
$ nslookup
> set type=txt
> selector_domainKey.test.com

Non-authoritative answer:
selector._domainKey.test.com	text = "v=DKIM1;k=rsa;p=<공개키>"

이를 위해서는 다음의 항목이 사전에 준비되어 있어야 한다.

  • selector (s= 태그값)
  • domain name (d= 태그값)

DKIM-Signature 헤더를 생성하기 전에 다음의 사항들은 미리 결정되어 있어야 한다.

  • 서명에 추가할 헤더 필드 목록 (h= 태그값)
  • 헤더/본문 정규화 알고리즘 (c= 태그값)
  • 해시 알고리즘 (a= 태그값)

서명자는 다음의 순서대로 DKIM-Signature 헤더를 생성한다.

  • bh=<빈문자열> 이 세팅되고 h, b 태그를 제외한 다른 모든 태그들이 미리 설정된 DKIM-Signature 헤더를 미리 생성해 둔다.
ex)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; t=1680846077; i=@test.com; 
	bh=<빈문자열>
  • 본문 정규화 알고리즘을 사용하여 메시지 본문을 정규화 후에 l= 태그가 지정된 경우 지정된 길이로 잘라낸 후 해시 알고리즘을 사용하여 메시지 본문에 대한 해시를 생성한다.
  • 생성된 해시 값을 base64로 인코딩 후 DKIM-Signature 헤더의 bh= 태그의 값으로 삽입한다.
ex)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; t=1680846077; i=@test.com; 
	bh=AC7JjovhH77RvEXqOtcReF5+pBjnu7glVCsWM5ALEID=;
  • DKIM-Signature 헤더의 h= 태그에 서명에 추가할 헤더 필드 이름 목록을 추가하고 값이 없는 b 태그(b=<빈문자열>) 를 추가한다.
ex)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; t=1680846077; i=@test.com; 
	bh=AC7JjovhH77RvEXqOtcReF5+pBjnu7glVCsWM5ALEID=; h=FROM:TO:SUBJECT:DATE; 
	b=<빈문자열>
  • h= 태그에 지정된 헤더 필드 목록을 지정된 순서대로 c= 태그에 지정된 헤더 정규화 알고리즘을 사용하여 정규화를 한다.
  • b= 태그의 값을 빈 문자열로 세팅해 둔 현재 생성 중인 DKIM-Signature 헤더를 c= 태그에 지정된 헤더 정규화 알고리즘을 사용하여 정규화를 한다.
  • 정규화된 헤더 필드 + 정규화된 DKIM-Signature 데이터를 a= 태그에 지정된 해시 알고리즘으로 해시를 생성한 뒤 개인키를 사용하여 암호화하고 base64로 인코딩한다.
  • 생성한 데이터를 DKIM-Signature 헤더 필드의 b= 태그에 값을 삽입한다. (최종 DKIM-Signature)
ex)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=test.com; s=selector; t=1680846077; i=@test.com; 
	bh=AC7JjovhH77RvEXqOtcReF5+pBjnu7glVCsWM5ALEID=; h=FROM:TO:SUBJECT:DATE; 
	b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR

 

간단히 서명 알고리즘을 공식으로 표현하면 다음과 같다.

body-hash = hash-alg(canon_body)
header-hash = hash-alg(canon_header + canon_DKIM-SIG) --> 여기서 DKIM-SIG는 b=<빈문자열> 상태다.
signature = sig-alg(header-hash, privateKey)

 

 

DKIM 서명 검증

  • 서명자는 언제든지 공개키를 제거하거나 해지할 수 있으므로 MTA에서 메일을 수신한 직후에 검증을 수행하는 것이 좋다.
  • 검증을 수행하는 MTA는 검증되지 않은 메일에 대한 정책을 구현할 수 있다.

다음의 단계를 통해 DKIM 검증을 수행한다.

 

1. DKIM-Signature 헤더 유효성 검사

  • DKIM-Signature 헤더의 필수 태그가 누락된 경우 PERMFAIL을 리턴한다.
    • 필수 태그는 다음과 같다.
      • v=, a=, b=, bh=, d=, h=, s=
  • DKIM-Signature 헤더 필드에 i= 태그가 포함되어 있지 않은 경우 해당 태그의 값이 @d 인 것처럼 동작한다. (d는 d= 태그의 값)
  • DKIM-Signature 헤더 필드에 i= 태그가 지정된 경우 i= 태그의 도메인 부분이 d= 태그의 도메인과 같거나 서브도메인인지 확인해야 한다. 그렇지 않은 경우 PERMFAIL을 리턴한다.
  • h= 태그에 From 헤더 필드가 포함되지 않은 경우 PERMFAIL을 리턴한다.
  • x= 태그가 지정되어 있고 서명이 만료된 경우 PERMFAIL을 리턴한다.
  • d= 태그에서 com, co.uk 와 같은 유효한 서명 엔터티와 연결되지 않는 경우 DKIM-Signature 헤더 필드를 무시할 수 있다.

 

2. 공개키 조회

  • DKIM-Signature의 q= 태그(default dns/txt)에 정의된 쿼리 유형에 따라서 s= 태그(selector), d= 태그(domain)의 값을 조회하여 조회할 수 있다.
  • 공개키에 대한 쿼리에 응답이 없는 경우 TEMPFAIL을 리턴하고 경우에 따라서 451/4.7.5 응답 코드로 응답한다.
  • 해당 키 레코드가 존재하지 않는 경우 PERMFAIL을 반환한다.
  • 공개키에 대한 쿼리가 여러 키 레코드를 반환하는 경우 키 레코드 중 하나를 선택하거나 키 레코드를 순회하면서 검증을 수행할 수 있다.
  • 쿼리에서 반환한 결과가 정의된 형식을 준수하지 않은 경우 PERMFAIL을 리턴한다.
  • 레코드의 g= 태그가 DKIM-Signature의 i= 태그의 로컬 파트와 일치하지 않으면 PERMFAIL을 리턴한다.
    • DKIM-Signature 의 i= 태그의 로컬 파트가 없는 경우 g= 태그는 “*” (도메인의 모든 주소에 유효 - default 값) 이거나 전체 g= 태그가 생략되어야 한다.
  • 레코드에 h= 태그가 존재(DKIM-Signature 헤더 태그가 아니다) 하고 DKIM-Signature 헤더의 a= 태그에 지정된 해시 알고리즘이 h= 태그(DKIM-Signature 헤더 태그가 아니다)의 내용에 포함되지 않은 경우 PERMFAIL을 리턴한다.
  • 레코드에 p= 태그가 비어 있으면 이 키는 해지된 것이며 PERMFAIL을 리턴한다.
  • 레코드에 k= 태그(default “rsa”)에 정의된 값과 DKIM-Signature의 a= 태그에 지정된 해시 알고리즘 유형과 일치하지 않은 경우 PERMFAIL을 리턴한다.
    • ex) 레코드에 k=”rsa”; h=”sha256” 이 정의된 경우 DKIM-Signature 의 a= 태그 값은 rsa-sha256 이어야 한다.

3. 서명 검증하기

  • bh= 태그 검증
    • c= 태그에 지정된 본문 정규화 알고리즘, l= 태그에 지정된 본문 길이를 통해서 본문을 정규화한다.
    • a= 태그에 지정된 해시 알고리즘으로 정규화 된 본문 데이터에 대한 해시 값을 구한다.
    • DKIM-Signature의 bh= 태그의 값을 base64로 디코딩하여 이전 단계에서 구한 해시 값과 비교한다.
      • 비교가 일치하지 않는 경우 PERMFAIL을 리턴한다.
  • b= 태그 검증
    • DKIM-Signature 헤더 전체를 복사하여 b=<서명값>을 b=<빈문자열>로 치환하여 별도의 메모리에 저장한다. (이하 DKIM 헤더 복사본)
    • c= 태그에 지정된 헤더 정규화 알고리즘, h= 태그에 지정된 헤더 필드 목록을 이용하여 헤더 데이터 정규화를 수행한다.
    • DKIM 헤더 복사본에 대해서 헤더 정규화를 수행한다.
    • 정규화된 헤더 데이터 + 정규화된 DKIM 헤더 복사본 데이터를 a= 태그에 지정된 해시 알고리즘으로 해시값을 구한다. - (1)
    • DKIM-Signature 헤더의 b= 태그에 지정된 서명값을 base64로 디코딩 후 공개키로 복호화한다. - (2)
    • (1)과 (2)의 값을 비교한다.
      • 비교가 일치하지 않는 경우 PERMFAIL을 리턴한다.

 

서명 검증 결과는 Authentication-Results 헤더를 생성하여 메시지 헤더에 추가한다.

이때 Authentication-Results 헤더는 현재 검증한 DKIM-Signature 헤더의 상단에 위치해야 한다.

ex)
Authentication-Results: mx.google.com;
       dkim=pass header.i=@test.com header.s=selector header.b=f4osEDyO;

 

 

사실 apache james의 jDKIM 라이브러리(java), opendkim 라이브러리(C)를 사용하여 DKIM의 기능을 구현할 수 있지만 상세 내용을 알고 사용하는 것과 모르고 사용하는 것의 차이는 크다고 생각하여 관련 내용을 정리해 보았다.

 

관련 라이브러리 링크는 다음과 같다.

 

Apache James Project – Download

Downloading Use the links below to download the product from one of our mirrors. You must verify the integrity of the downloaded files using signatures KEYS downloaded from our main distribution directory. (*) James maven repositories can be found on http

james.apache.org

 

OpenDKIM

 

www.opendkim.org

 


참고링크

https://dkim.org/specs/rfc4871-dkimbase.html

 

DomainKeys Identified Mail (DKIM) Signatures

[RFC4033]Arends, R., Austein, R., Larson, M., Massey, D., and S. Rose, “DNS Security Introduction and Requirements”, RFC 4033, March 2005.

dkim.org

 

댓글

💲 추천 글