SPFのDNS参照回数が10回を超える原因と直し方|PermErrorの数え方

SPFの診断結果に「DNS参照回数が上限を超えています」と表示されることがあります。これはSPFレコードが無いのとも、書き間違えているのとも違います。レコードの中身自体は正しくても、参照している先をすべてたどって数えると、DNSへの問い合わせが10回を超えてしまっている状態です。
この記事では、この参照回数の数え方と、超えてしまったときの直し方を扱います。「送信・受信のどちらが問題か分からない」という段階であれば、先に独自ドメインのメールが届かない原因の切り分けを確認してください。
診断結果が「DNS参照回数が上限を超えています」と出たら
SPFを診断すると、次のような形式で結果が返ります。
DNS参照回数が上限を超えています (13回 / 上限10回)
example.jp → include:_spf.google.com
_spf.google.com → include:_netblocks.google.com
_spf.google.com → include:_netblocks2.google.com
example.jp → include:spf.protection.outlook.com
spf.protection.outlook.com → include:spf-a.outlook.com
...
上の値はこの記事用の例です。実際に表示される回数と参照先は、ドメインごとのSPFレコードによって変わります。
最初の行が結論、その下が「どの参照で回数を消費したか」という証跡です。数字だけでなく、この証跡を見れば、どのincludeが原因になっているかを自分で判断できます。
見ての通り、この状態は「SPFレコードが設定されていない」とはまったく別です。レコードは存在し、構文も正しいのに、評価の途中で参照回数の上限に達し、受信側がそこで評価を打ち切っています。
SPF fail・PermError・DMARCの関係
SPFの評価結果にはいくつかの種類があり、PermErrorはそのひとつです(RFC 7208 §2.6)。
- fail は「送信元がこのドメインの利用を認可されていないという明示的な判定」です(RFC 7208 §2.6.4)
- PermError は「公開されているレコードを正しく解釈できなかった」という状態です(RFC 7208 §2.6.7)
failは判定した結果として送信元を拒否している状態で、PermErrorは判定そのものを完了できずに終わっている状態です。原因が違うため、直し方も違います。
DMARCを設定している場合への影響は次のとおりです。
SPFがPermErrorになると、SPF経由ではDMARCの認証を成功させられません。ただし、Fromアドレスのドメインと整合したDKIMの署名が成功していれば、DMARCは成功します。DMARCはSPFとDKIMのどちらか一方が、Fromドメインと整合した形で成功していれば通る仕組みだからです(RFC 7489 §4.2)。SPFがPermErrorになっているからといって、DMARCの判定も連動して失敗するとは限りません。
受信メールのヘッダーで実際の判定結果を確認したい場合は、Authentication-Resultsヘッダーにspf=permerrorやdmarc=passのように記載されます。診断ツールが確認できるのは公開されているDNSの設定までで、実際に届いたメールがどう判定されたかは、受け取った本人がヘッダーを見るまで分かりません。
PermErrorになる原因を切り分ける
PermErrorには参照回数の超過以外にもいくつかの原因があります。この記事で深く扱うのは1行目の「DNS参照回数の超過」だけです。
| 原因 | 根拠 |
|---|---|
| DNS参照回数が10回を超えている | RFC 7208 §4.6.4(MUST) |
| 応答が空、または存在しないドメインへの参照(void lookup)が2回を超えている | RFC 7208 §4.6.4(SHOULD) |
| SPFレコードが2本以上ある | RFC 7208 §4.5 |
| レコードの構文に誤りがある | RFC 7208 §4.6 |
redirectまたはexp修飾子が同じレコード内に2つ以上ある |
RFC 7208 §6 |
redirectの参照先にSPFレコードが無い |
RFC 7208 §6.1 |
10回の参照回数制限は「MUST」、2回の空応答制限は「SHOULD」という違いがあります(RFC 7208 §4.6.4)。前者は実装が必ず守らなければならない上限、後者は実装側で設定可能な推奨値という位置づけの違いです。本サイトの診断ツールはどちらも固定値(10回、2回)で判定しています。
以降は参照回数の超過に絞って説明します。
DNS参照回数10回の対象になる項
SPFレコードの中には、参照回数を消費する項と消費しない項があります(RFC 7208 §4.6.4)。
The following terms cause DNS queries: the "include", "a", "mx", "ptr", and "exists" mechanisms, and the "redirect" modifier.
日本語にすると、DNS参照を発生させるのはinclude・a・mx・ptr・existsの5つの機構と、redirect修飾子です。
| 参照を消費する | 消費しない |
|---|---|
include / a / mx / ptr / exists / redirect= |
ip4: / ip6: / all |
ip4:やip6:はIPアドレスをそのまま書くだけなのでDNSへの問い合わせが発生しません。一方、includeのようにドメイン名を指定する項は、そのドメインのSPFレコードを取得しに行くため参照を消費します。
ここで、本サイトの診断ツールが数えている回数の性質について明記しておきます。
数えているのは、レコードに書かれた全ての項を評価した場合の最大値です。実際にメールが送信されたときの評価は、途中でallにマッチした時点で打ち切られるため、実際に消費される参照はこれより少ないことがあります。逆に言えば、この最大値が10回以内に収まっていれば、実際の評価でも上限を超えることはありません。
また、a・mx・ptr・existsは「1項につき1参照」として数えており、参照先のドメインが持つAレコードやMXレコードの件数までは展開していません。これらの機構を使っている場合、実際の参照回数はここで示す数より多くなる可能性があります。
void lookup(応答が空、またはNXDOMAINだった参照)についても同様の注意が必要です。本サイトの診断ツールが数えているのは、ルートのレコード自体と、include・redirectで辿った先の応答が空だったかどうかです。a・mx・ptr・existsの参照先が空応答だったかどうかは、この集計に含まれていません。
includeの入れ子を含めて数える
参照回数を数えるときに見落としやすいのは、見た目の行数ではなく、includeの中でさらに発生する参照まで含めた合計だという点です。
自分のSPFレコードが次の1行だけだったとします。
v=spf1 include:_spf.google.com ~all
この状態ではincludeが1つなので、参照は1回に見えます。しかし_spf.google.com自身のSPFレコードが内部でさらに複数のincludeを使っていれば、その分もすべて合計に加算されます。自分のレコードを何も変更していなくても、参照先のサービスが内部構成を変えて参照が増えれば、ある日突然この上限に達することがあります。
redirectを使っている場合は、もう1点注意が必要です。SPFレコードにall機構がある場合、redirect修飾子は無視されます(RFC 7208 §6.1)。ただし本サイトの診断ツールでは、無視されると分かっているredirectであっても、参照回数のカウント自体は先に加算してから展開をスキップしています。実際の評価では効果を持たないredirectが、参照回数の集計には影響するという食い違いが起こり得ます。レコードにallとredirectが両方書かれている場合は、この点を踏まえて数えてください。
どの参照が上限を使っているか、証跡で確認する
診断結果には、合計回数だけでなく、どのドメインのどの項が参照を消費したかという一覧(証跡)が付きます。冒頭の例で示した
example.jp → include:_spf.google.com
_spf.google.com → include:_netblocks.google.com
という形式がそれです。左側が参照元、右側がその時点で消費した項を表します。この一覧を上から順に確認し、まだ使っているサービスかどうかを1つずつ照合してください。
自動で判定できないケースもあります。%{i}のようなマクロを含む項は、送信元のIPアドレスが分からないと展開できないため、参照回数としてはカウントしますが、その先の中身までは追っていません。このような項がレコードに含まれている場合、証跡だけでは全体像を確認しきれないことがあります。
手動で数え直す方法
診断ツールを使わずに、digコマンドで同じことを手作業で確認できます。
dig +short txt example.jp
まず自分のドメインのSPFレコードを取得し、include:で指定されているドメインを書き出します。次に、その各ドメインに対して同じコマンドを実行します。
dig +short txt _spf.google.com
これを、includeが出てこなくなるまで再帰的に繰り返し、出てきたinclude・a・mx・ptr・exists・redirect=の数を合計すれば、参照回数を自分で検算できます。レコードの階層が深い場合はこの作業自体が大変になりますが、診断ツールが返す証跡と照らし合わせて、数え方に食い違いがないかを確認する手段として使えます。
安全な直し方
参照回数を減らす方向で直します。
- 使っていないサービスの
includeを削除する。契約を解約したサービスのincludeが残っていることがあります - 送信元のIPアドレスが固定されているサービスに限り、
include:をip4:に置き換える。ただしサービス側がIPアドレスを変更した場合にメールが届かなくなるため、変更の通知を受け取れる契約になっているサービスに限ります - 利用している送信サービスの数自体を見直す
SPFレコードを2本に分けるのは解決になりません。SPFレコードは1本しか置けず(RFC 7208 §4.5)、2本あるとその時点で受信側はPermErrorと判定します。参照回数の上限を回避する目的でレコードを分割すると、状況が悪化します。
送信サービスを1つ追加すると参照回数も増えます。現在の回数が上限に近い場合、次にサービスを追加するタイミングで超過する可能性があることも踏まえて整理してください。
自動で修正値を出さない理由
本サイトの診断ツールは、他の所見では設定に貼り付けられる値を自動生成しますが、参照回数の超過についてはその値を出しません。
理由は、どのincludeが現在も実際に使われている送信サービスかを、公開されているDNSの情報だけから判断できないためです。使われていないように見えるincludeを自動で削除する値を提示すると、実は稼働中の送信元を誤って除外してしまう可能性があります。実際に使っているサービスかどうかは、契約状況を知っている運用者にしか判断できません。上の証跡を見ながら、1つずつ照合して削除の判断をしてください。
修正後に再診断する
レコードを修正したら、再度診断して参照回数が上限内に収まっているかを確認してください。
修正した直後は、DNSの反映を待っている再帰リゾルバが残っている場合があります。再診断で古い値のまま表示される場合は、設定を変更する前にまず反映状況を確認してください。