雑文発散

«前の日記(2014-02-02) 最新 次の日記(2014-02-04)» 編集
過去の日記

2014-02-03 [長年日記]

[PHP] PHP の Swift Mailer で DKIM 署名付きメールの送信をする

Swift Mailer の実装をつらつら眺めていたら、なんか DKIM を使ったメール送信ができそうだったので試してみた。

DKIM というのはメールの送信者認証技術のひとつで、公開鍵認証を使って、メール送信者のなりすましを防ぐもの。詳しい説明は詳しいところで確認をした方が間違いがないので、間違いがないように確認した方が良い。

今回のシナリオとしては、suzuki@example.com から送信するメールに DKIM の署名を付けてみようというもの。言うまでもないと思うけど、example.com は例示用のドメインなので、下記のやり方を実践する場合には、自分で所有しているドメインと読み替えが必要。


まず、公開鍵の作成用に opendkim-tools パッケージをインストール。あ、OS は Debian wheezy で検証している。

% sudo apt-get install opendkim-tools

opendkim-tools パッケージの中に opendkim-genkey コマンドが入っており、これで秘密鍵と公開鍵が作成できる。さらに公開鍵は DNS に設定するフォーマットで作成してくれるので、だいぶお手軽。

-d domain オプションでドメイン名を指定しておく。ただ、opendkim-genkey のマニュアルを見ると、現状では生成したファイルのコメントに使われるだけのようだ。

% opendkim-genkey -d example.com

実行すると2つのファイルができる。

% ls -1
default.private
default.txt

default.private が秘密鍵のファイル。中身はこんな感じ。でも、秘密なので普通は公開しちゃダメなヤツ。これはダミーのヤツで、実利用はしていないもの。あと途中を省略しているので使えない。

% cat default.private
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDKawKkF6yEYxyrn+t8P0wF0Ez23UTI4KqM6BAdf1Xu3Bus7/+s
[snip]
TM7rqUwdWpGsjBwtTvy8MY+lsPriw81y5fKMynp2k+p7
-----END RSA PRIVATE KEY-----

もうひとつの default.txt が DNS の TXT レコードに登録するフォーマットで書かれた公開鍵。

% cat default.txt
default._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0 [snip] IDAQAB" ;\
  ----- DKIM key default for example.com

この公開鍵は default._domainkey.example.com の TXT レコードに設定する。example.com の TXT レコードではないことに注意。

このサブドメイン default の部分は、selector と呼ばれるところ。ひとつのドメインで複数の公開鍵を設定できるような規格になっている。今回は default という selector 名を使ったということ。

利用している DNS サーバへ default.txt の記述をよしなに設定して、dig コマンドなどで default._domainkey.example.com の TXT レコードを引いて確認する。

オレが試したときは Route 53 を使ったのだけど、設定直後に dig コマンドで検証しても最初は TXT レコードが返ってこなかった。Web インターフェイスで設定してから、全ての NS サーバへ反映されるまで、ちょっとだけタイムラグがあるのかもね。

% dig default._domainkey.example.com txt +noall +answer

; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> default._domainkey.example.com txt +noall +answer
;; global options: +cmd
default._domainkey.example.com. 300 IN  TXT "v=DKIM1\; k=rsa\; p=MIGfMA0 [snip] IDAQAB"

ここまでで DKIM の DNS 側の準備が終了。


次に Swift Mailer の準備。今回は Composer でインストールする。使った composer.json の内容はこれだけ。現時点の Swift Mailer の最新版は 5.0.3 なんだけど、マイナーバージョンが上がった場合にも対応できるように "5.0.*" の表記にしている。

Composer の取得方法はマニュアルにまかせて、Swift Mailer をインストールする。

% ./composer.phar install

これで Swift Mailer の準備は完了。


ようやく PHP のコード。

まずは Swift Mailer を使って、通常のメール送信を行なうコード。メールの送信先は、下記のコードの YOUR_GMAIL_ADDRESS の部分で指定している。実際の検証時には自分の Gmail アドレスに置き換える。

これを元に DKIM の署名付きメールを送信するように書き換えてみるとこうなった。数行の追加・修正程度。

これを実行し、Gmail で受信したメールのソースを見てみると、メールヘッダに DKIM-Signature があるのが分かる。

DKIM-Signature: v=1; a=rsa-sha1; bh=bAOD/kGTEevQnIsv2dOuX+gmZhw=;
 d=example.com; h=Message-ID: Date: Subject: From: To: MIME-Version:
 Content-Type: Content-Transfer-Encoding; i=@example.com; s=default;
 t=1391351009;
 b=OuC8oU789v943Qkz2GzZ+YP7RFCVhRZkf+rJtR+YUAgoes3FQOvp+3/hE3hyAzDXSs0kBL5fC
 kCtFLabd+jAunzL0p8gJ07xYHnTgK0g87zEBVHiolrqr/UXjMYoR2pNl1VdOFy6mBdYbUz4Q0
 7vra/FoykWEmMGTaFUnAKUo1c=

これが DKIM の署名。v=1 だとか d=example.com だとか、各々のパラメータの意味は「送信ドメイン認証技術導入マニュアル第2版」を読むと分かると思う。

更にメールヘッダ内には、Authentication-Results ヘッダも存在する。

Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of suzuki@example.com designates nnn.nnn.nnn.nnn \
       as permitted sender) smtp.mail=suzuki@example.com;
       dkim=pass header.i=@example.com

こいつは受信サーバによる検証結果を格納するヘッダで、dkim=pass というのが「DKIM によるチェックで送信者(送信サーバ)にパスしている(問題がない)」という意味になっている。

spf=pass というのもあるが、これは別途 SPF による送信者(送信サーバ)チェックにパスしている結果を表している。

SPF と DKIM は、双方とも「送信者認証技術」にカテゴライズされるんだけど、それぞれ得意・不得意があるので、「可能ならば両方同時に利用してメール送信した方が良い」というのが、迷惑メール対策推進協議会の意見だったはず。

また、DMARC でもその方針のようだ(こっちは詳しくない)。

元々 Swift Mailer を使っているプロダクトだったら、今までのコードに数行の追加・修正を行なう程度で DKIM 送信ができてしまうので、使わない手は無いんじゃないかな、と思う。

ただし、1通の送信にかかるコストは高くなる(署名する分が重い)のと、DNS に負荷がかかる(受信側サーバからの問い合わせが増える)ので、プログラムだけではなく、システム全体とのバランスは考慮する必要がある。

その辺の負荷が問題なさそうなら、DKIM 署名付きで送信した方が、受信側で spam 判定されにくくなるかもしれない。

本日のツッコミ(全1件) [ツッコミを入れる]
Marty Hermsen (2015-08-12 21:51)

Suzuki Thanks for this great example of integration DKIM in Swiftmailer 5, I implemented last night in my production environment. I will follow you on Github because my(our) next step is the use of DMARC in Swiftmailer !!