|||||||||||||||||||||

なんぶ電子

- 更新: 

SPFとDKIMとDMARC

DKIM

Goolge宛のメールが時折届かないことがあるのは知っていたのですが、迷惑メールフィルターか何かに引っかかっているんだろうと、気にせずにいました。しかし、最近その頻度があがったので重い腰を上げ対応することにしました。

メール認証

Googleでは、2022年11月からメール認証をするようになりました。

ランダムで認証を行い通過しないものに関しては、迷惑メールに分類されるか、メールが届きません。

認証に失敗した場合は、送信者には次のようなメッセージの不達通知が届きます。

This message does not pass authentication checks (SPF and DKIM both do not pass). SPF check for [メールドメイン] does not pass with ip:[IPアドレス].To best protect our users from spam, the message has been blocked.

認証はSPFと、DKIMによって行われます。

SPF(Sender Policy Framework)

SPFとは何かについてはリンク先のJPNICのページにありますが、メールの送信元が偽装されていないかチェックするためのしくみです。

メールは構造上Fromアドレスを自由に設定可能なためなりすましが容易です。

SPFはそのなりすましを防止するためのひとつの手段です。

SPFはDNSに設定し、どのネットワークから送信されるべきかを指定します。

メールを受信した受信サーバーは、DSN中のSPFレコードを確認して、差出人のメールサーバーのIPアドレスと照合します。ここで指定されていない差出人の場合は不正なメールとして拒否をします。

これは受信側のサーバー頼りのセキュリティです。また、なりすましメールの送信自体を防ぐものではありません。

SPFレコードの基本的な設定の仕方は次のようになります。

これはDNSのSPFレコードに設定するルールですが、SPFレコードに対応しないDNSサーバーの互換性のためにTXTレコードに同内容のレコードを持つのが慣例となっています。

テキストレコードはの中身は次のようにします。DNSマネージャーの形態を持つ場合は自動で付与してくれることもありますが、多くの場合TXTレコードの値は"で囲います。

"v=spf1 ip4:192.168.1.0/24 ip4:192.168.2.254 -all"

たとば、過去にこの記事として取り上げたBIND9で設定するなら次のようになります。

marune205.jp. 3600 IN TXT "v=spf1 ip4:192.168.1.0/24 ip4:192.168.2.254 -all"

最初のmarune205.jp.の部分は、BINDではownerと名付けられていますが、この記事内では「ホスト名」と呼びます。

このホスト名は、もしメールがサブドメインから送られる場合はサブドメインを指定します。

続いいてテキストの部分を解説していきます。

v=spf1はバージョンを意味して最初に記述します。

バージョンの後は、判定条件をスペースで区切って記述していきます。

先の例では、ip4:としてIPv4アドレス範囲で192.168.1.0ネットワークからの送信を許可しています。

その後、192.168.2.254のIPアドレスを許可しています。

その後の-allですが、-は承認しないという意味です。allは文字通りすべてのアドレスを表します。

左からチェックしていき、最初に一致した条件の結果が返る仕組みになっていますので、全体として192.168.1.0/24でも192.168.2.254でもなければ、認証しないという意味になります。

IPv6で指定したい場合は、ip6:、ドメインで指定したい場合はa:を使います。ドメインで指定した場合は、受信側でDNSの問い合わせをして得られたIPアドレスと送信者のIPアドレスを照合します。

-はall以外でも使えます。また-のかわりに~を使うと正当なアドレスであっても認証に失敗する可能性があることを示します。

この-や~は「限定子」と呼ばれます。先のふたつは拒否を意味しますが、他に許可を意味する+があります。先の記述ではip4:やa:としましたが、これらは+ip4:や+a:省略記法です。

くれぐれも、エラー回避のためだけに+allとしないようにしてください。迷惑メールの踏み台になります。

限定子に応じて、認証の結果が分かれますが、この結果は「Pass(成功)、Fail(失敗)」の他に「SoftFail(失敗だけれども失敗の判定が間違いの可能性がある)とNutral(評価しない)」があります。SoftFailはさきほどの~による拒否を示し、Nutralは条件のいずれも一致しなかった場合か?限定子を付けた条件に一致した場合に返されます。

他、詳しい記述方法はIA japan:SPFのページにあります。

DKIM(DomainKeys Identified Mail)

DKIMは電子署名を利用してメールの本文やヘッダが改ざんされていないかをチェックする仕組みです。

SPFで送信元の正当性をチェックすることができますが、中身の改ざんは検知できません。そこでDKIMにより中身の改ざんのチェックを行います。

一方のDKIMは、電子署名により送信者や内容に変更がないことを保証できますが、メールがどこから送信されたか(送信元のIPアドレス)はチェックすることができません。そのためSPFと組み合わせて用いられます。

DKIMを運用するには、メールに署名する機構の設定と、署名を検証するための公開鍵をDNSに登録するふたつの作業が必要です。

署名に関してはメールサーバーで行うようにすれば管理が楽ですが、必ずしもメールサーバーで行わなければならないというものではありません。

またDNSへの登録はTXTレコードに設定します。

メールを受信したサーバーは、メールに添付されている署名とDNSのDKIM用のレコード(公開鍵)を使ってメールの改ざんを検知します。

PHPMailerでDKIM

DKIMの運用をしようとしたとき、メールサーバーがpostfilxなら、milterとしてopendkimを設定する方法がありますが、サーバー側でそのようなDKIMの設定ができないなら、接続クライアント側で行うこともできます。PHPMailerを使えば簡単にDKIMの署名をすることができます。

まず、鍵のセットをもっていない場合は作成します。これはOpenSSLで作成が可能です。

秘密鍵を作成します。

openssl genrsa -aes256 -out dkim.key 2048

秘密鍵と対になる公開鍵を作成します。

openssl rsa -in dkim.key -pubout -out dkim.pub

作成した秘密鍵を、PHPMailerに組み込みます。本番運用する鍵の場合は取り扱いに気を付けてください。

以前作成したPHPMailerのコードにDKIM用の設定を追加することで署名ができます。

sendmail.php


function php_sendmailWithDKIM($strTitle, $strMsg) {
  mb_language("japanese");
  mb_internal_encoding("UTF-8");
 
  $mailer = new PHPMailer();
 
  try {
   //ログ出力と、保存の設定
   $mailer->SMTPDebug =2; //SMTP::DEBUG_SERVER定数を利用するならSMTPクラスのインポートが必要です。
   $mailer->Debugoutput = function($str, $level) { file_put_contents("phpmailer.log",$str); };
   
   //DKIM設定
   $mailer->DKIM_domain = 'ドメイン';
   $mailer->DKIM_private = './dkim.key'; // 秘密鍵へのパス
   $mailer->DKIM_selector = 'selector'; //DKIMセレクタ セレクタ._domankey.ドメインという形でDNSへ登録します。これにより複数の公開鍵の設定が可能になります
   $mailer->DKIM_passphrase = '秘密鍵パスワード';
   $mailer->DKIM_identity = "メールアドレス"; //通常は差出人のメールアドレスです。$mail->Fromとする場合は値をセットした後に行います
   $mailer->DKIM_copyHeaderFields = false; //z=認証対象ヘッダコピーを付与しない(デバッグ時はtrueにすると便利です)

   //SMTP設定
   $mailer->IsSMTP();
   $mailer->Host = "メールサーバー";
   $mailer->SMTPAuth = true;
   $mailer->Username = "ユーザー名"; 
   $mailer->Password = "パスワード";
   $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
   $mailer->Port = 587;
   
   $mailer->CharSet = 'utf-8';//メッセージのエンコードを指定
   $mailer->setFrom("送信者アドレス", "送信者名"); 
   $mailer->addAddress("受信者アドレス", "受信者名");
    
   //本文
   $mailer->isHTML(true);//HTML形式のメールに
   $mailer->Subject = mb_convert_encoding($strTitle,"utf-8","auto");
   $mailer->Body = mb_convert_encoding($strMsg,"utf-8","auto");
 
   return $mailer->send();//送信実行
    
  } catch(Exception $e) {
   //エラー
   makeLog($e->getMessage());
   return false;
  }
}

送信したメールに次のようなDKIM-Signatureヘッダが付与されます。

DKIM-Signature: v=1; d=設定したドメイン; s=設定したセレクタ;
a=rsa-sha256; q=dns/txt; t=1674019520; c=relaxed/simple;
h=Date:To:From:Subject:Message-ID:X-Mailer:MIME-Version:Content-Type;

DKIM利用時のDNSサーバーへの登録

DKIM利用時はDNSサーバーのTXTレコードにふたつの設定をします。ひとつは必須の公開鍵の設定です。

まず、ホスト名を

セレクタ._domainkey.ドメイン

とします。間の_domainkeyの部分は固定値です。セレクタはPHPMailerに設定する値と同じにします。ドメインは自身のドメインです。

値は

"v=DKIM1; k=rsa; p=公開鍵の中身"

とし、先のようにBINDで設定するなら

セレクタ._domainkey.marune205.jp. 3600 IN TXT "v=DKIM1; k=rsa; p=公開鍵の中身"

となります。

DNSサーバーの仕様で、公開鍵は入りきらない場合は分割して複数のレコードにします。

もうひとつの設定は、ADSPレコードといいます。主にDKIMによる署名がどのように行われているかを受信者に知らせるものです。設定をするのが丁寧だとおもいますが、gmail.comにも設定はありませんでした。

ホスト名を、_adsp._domainkey.ドメインを使って次のいずれかを設定します。

  • dkim=all

    ドメインから送信されるメールはすべて電子署名がつけられているという意味になります。

  • dkim=unknown

    電子署名がつけられていたり、つけられなかったりする場合はunknownを設定します。

  • dkim=discardable

    すべて電子署名がつけられいるのに加え、署名が確認できない場合は破棄すべきという意味になります。

今回はPHPMailerを使わない送信は署名されないので、unknownを設定します。BIND9なら次のようになります。

_adsp._domainkey.marune205.jp. 3600 IN TXT "dkim=unknown"

DKIMの設定に関してもIA japanのサイトに詳しい説明があります。

署名の検証

DKIM署名の検証は手動でも論理上は可能ですが、筆者は断念しました。ThunderbirdのDKIM Verifierのようなツールを使うと簡単に検証することができます。

また先ほどPHPMailerで設定した$mailer->DKIM_domainがメールアドレスのドメインと違う場合は、$mailer->DKIM_identityの部分をコメントアウトすれば、署名管理の部分だけ違うドメインで管理することも可能です。(警告はでます)

DKIM

DMARC

DMARCはDomain-based Message Authentication, Reporting and Conformanceの略です。

SPFやDKIMはともに受信サーバーがチェックをするため、異常を検知したメールの取り扱いをどうするかは受信者にゆだねる形になっています。

DMARCは、SPFやDKIM認証の結果の取扱いをドメイン所有者が指定できるのに加え、受信者側のメールの認証結果をレポートとして受け取ることができるものです。

何度も紹介しているIA japanが発行している「DMARCによる新しいメール認証と導入の留意点」によればこの設定もDNSに行います。

DMARCにおけるDNS設定は次のようなります。

_dmarc.marune205.jp. 3600 IN TXT "v=DMARC1; p=none; rua=mailto:dmarc-feedback@marune205.jp"

vの値はバージョンです。次のpはポリシーです。これは先ほどのDKIMの_adspレコードに似ていますが、異常を検知した場合どのように取り扱うべきかを指定します。

  • none

    なにもしない

  • quarantine

    隔離(迷惑メールとして格納)

  • reject

    拒否

ruaにはmailto:というプレフィックスを付けて、集約レポートの送信先であるメールアドレスを設定します。

rufとすると集約レポートではなく、失敗レポートの指定となります。ruaとrufの両方を設定することも可能です。

また先の例には載っていませんが、pct=の値を設定すると、異常を検知したメールに対してポリシーを適用する割合(%)を決められます。たとえばpct=10なら、異常を検知メールのうち10%に対してポリシーで設定した処理をするように指示をします。

これらの設定も受信サーバー頼りのものになりますので、拒否すべきメールが拒否されなかったり、レポートが来なかったりすることを承知しておかなければいけません。

spとするとサブドメインのポリシーを設定できます。基本的にサブドメインのポリシーが設定していない場合は、ドメインのポリシー(p)の値が採用されます。

spの使い方は、GoogleのDMARCの値を例にとって説明します。この記事を執筆した時点のGoogleにおけるDMARCレコードは

"v=DMARC1; p=none; sp=quarantine; rua=mailto:mailauth-reports@google.com"

となっていました。

これはxxx@gmail.comから送信されたメールはSPFやDKIMの認証に通過しないメールに関してはGoogleは何も指示をだしておらず、受信者にゆだねられている状態です。ただ、xxx@fake.gmail.comやxxx@dummy.gmail.comといったサブドメインから送られたメールは(実際には存在しないはずなので)隔離するように案内している状況です。

DMARCのレポートを利用するだけなら先のDNSレコードの設定だけで済みますが、DMARCに完全に対応するにはメールサーバー側にレポートを送信する機構を付与しなければいけません。そのようなDMARCの実装には、opendmarcライブラリがあります。

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告