SPFのルックアップ制限

SPFのDNSルックアップ10回制限:「too many DNS lookups」(permerror)の原因と直し方

どの項目も正しく見えるのにSPFがpermerrorになるのはなぜか。DNSルックアップを消費する項目、入れ子のincludeをたどった数え方、voidルックアップの別枠の上限、正当なメールを失わずに10回以内へ戻す方法をまとめます。

ミントに光るガラスビーズが並ぶ銅のフレームと、細い糸でつながった小さなアイボリーの陶器の塔、棒に収まらない灰色のビーズ1つと銅のノギスを描いたコンセプトイラスト。
概念を表すイラスト

SPFレコードには、メールサービス、メルマガ配信ツール、CRMのincludeがいくつか並び、最後は-allで終わっています。見た目には問題ありません。それなのに、メールヘッダーにspf=permerrorと出る、DMARCレポートにSPFのエラーが並ぶ、チェックツールが「too many DNS lookups」と警告する。原因の多くは、SPFの規格が定める「DNS問い合わせを伴う項目は10個まで」という上限です。この上限は、includeの参照先に隠れている問い合わせまで数えます。

この記事では、上限に数える項目と数えない項目、別枠で決められているvoidルックアップの上限、合計を手で数える方法、正当なメールを失わずに10回以内に戻す方法、フラット化に注意が必要な理由、そして1つの名前にSPFレコードを1つしか置けない理由を順に説明します。ドメイン名とIPアドレスはすべて例です。

このガイドの対象範囲

無料の公開準備スナップショットは、入力したホスト名そのもののSPFレコードを読み、そのレコードに書かれたDNS問い合わせを伴う項目だけを数えます。include:やredirect=はたどらず、voidルックアップも数えず、メールも送信しません。ペネトレーションテストではありません。

「too many DNS lookups」とは何か

受信側のサーバーはSPFを確認するとき、エンベロープ送信者(MAIL FROMのアドレス。配信後はReturn-Pathとして見えます)のドメインにある、v=spf1で始まるTXTレコードを読み、項目を左から順に評価します。項目の中には、受信側に追加のDNS問い合わせをさせるものがあります。RFC 7208の4.6.4節は、こうした項目を1回の確認につき10個までに制限しており、includeで参照したレコードの中の項目も数に含めます。評価に11個目が必要になった時点で、受信側は処理を止めてpermerrorを返さなければなりません。permerrorは「公開されているレコードを正しく解釈できなかった」という結果です。

この問題に気づくのは、たいてい次の3か所のどれかです。配信されたメッセージのAuthentication-Resultsヘッダー(RFC 8601)にspf=permerror smtp.mailfrom=example.comのように記録される場合、ルックアップが多すぎると書かれたバウンスメールが届く場合、そしてDMARC集計レポートにSPFのエラーとして現れる場合です。

この不具合が分かりにくい理由は二つあります。一つは、受信側が評価しながら問い合わせを数え、最初に一致した項目で評価を終えることです。レコードの前のほうに書かれたサービスのメールはpassし続ける一方、10回目の問い合わせより後ろにincludeがあるサービスのメールだけがpermerrorになるため、症状が出たり出なかったりするように見えます。もう一つは、permerrorがfailとは違うことです。SPFが判定の役に立たなくなる、というだけです。DMARCは、From欄のドメインと整合(アライメント)したドメインでSPFまたはDKIMがpassすれば合格です(RFC 9989)。整合したDKIM署名があるメッセージは引き続き合格し、SPFだけに頼っていたメッセージは合格しなくなります。それ以上にpermerrorをどう扱うかは、受信側ごとの判断です。

上限に数える項目、数えない項目

項目10回の上限に数えるか補足
include:1回。参照先レコード内の問い合わせもすべて加算参照先の名前にSPFレコードがないと、結果はpermerror
a、a:1回送信元がIPv4とIPv6のどちらで接続したかに応じて、その名前のAまたはAAAAレコードを問い合わせる
mx、mx:1回返ってきたMXホストのアドレス問い合わせには、これとは別に10件の上限がある
ptr1回RFC 7208は公開すべきでない(SHOULD NOT)としている
exists:1回組み立てた名前にAレコードがあれば一致。マクロと組み合わせて使われることが多い
redirect=1回。転送先レコード内の問い合わせもすべて加算レコードにallがあると無視される
ip4:、ip6:0回アドレスをそのまま比較し、問い合わせはしない
all0回それまでに一致しなかったすべての送信元の結果を決める
exp=0回failになった後、説明文を取得するときだけ問い合わせる
自分のv=spf1レコード0回最初のTXT問い合わせは項目に数えない

上限は1つのレコードごとではなく、評価全体に対してかかります。includeは、それ自体で1回、さらに参照先のレコードが消費する分を、入れ子の一番深いところまで加算します。プロバイダーは自社のレコードを変更することがあり、たとえばアドレス範囲をより多くの入れ子のincludeに分けることもあります。そうなると、こちらが何も編集していなくても合計が変わります。

この上限があるのは、SPFレコードが、公開した側に代わって受信側にDNS問い合わせを行わせる仕組みだからです。RFC 7208の11.1節は、上限がなければ攻撃者がこれを悪用して第三者のDNSに大量の問い合わせを送りつけられることを説明しています。RFCは確認全体にかける時間の上限(少なくとも20秒)も勧めています。時間切れの結果はpermerrorではなく、一時的なエラーであるtemperrorです。

もう一つの上限:voidルックアップは2回まで

RFC 7208は、空の応答が返ってきた問い合わせにも別枠の上限を設けています。レコードが1件もない応答(応答件数0のNOERROR)と、名前が存在しないというName Error(NXDOMAIN)です。これらをvoidルックアップと呼びます。実装は2回までに制限すべき(SHOULD)とされ、超えた場合は合計が10回未満でもpermerrorになります。存在しない名前を大量に問い合わせさせるようなSPFレコードを防ぐための上限です。

voidルックアップは、たいてい古い設定の消し忘れから生まれます。

  • 旧サーバーのDNS名を削除した後も残っているa:old-web.example.com。
  • MXレコードのない名前に対するmxやmx:example.org。この場合、mxはその名前のAレコードで代用してはならないと定められているので、単に何も見つかりません。
  • Webサイトをwwwでしか応答しないホスティングに移した後も、ドメイン直下に対して残っているa。

include:はさらに厳格です。参照先の名前が存在しない、またはSPFレコードを公開していない場合、そのincludeは単なるvoidルックアップとして数えられるのではなく、評価結果全体を即座にpermerrorにします(5.2節)。使わなくなったサービスが、こちらのレコードでまだincludeしているドメインを廃止したときに起きるのがこれです。レコードが参照しているすべての名前を問い合わせて確認し、何も返らない名前があれば修正するか削除してください。

SPFのルックアップ回数を手で数える方法

まず、自分が送ったメッセージのReturn-Pathヘッダーでエンベロープ送信者のドメインを確認し、そこからレコードを再帰的にたどります。

  1. レコードを読みます。dig +short TXT example.com、Windowsならnslookup -type=TXT example.comです。
  2. include、a、mx、ptr、exists、redirectはそれぞれ1、ip4、ip6、allは0と数えます。
  3. includeとredirectは、参照先のレコードを読んで同じように数えます。入れ子が続く限り、一番深いところまでたどります。
  4. すべてを合計し、レコードが返らなかった名前を書き留めます。

次の仮のレコードは、見た目にはDNS問い合わせを伴う項目が5つしかありません。

example.com.  TXT  "v=spf1 mx include:_spf.mail.example.net include:spf.crm.example.org include:news.example.net a -all"
項目ルックアップ回数内訳
mx1example.comのMX問い合わせ
include:_spf.mail.example.net4include自体で1、そのレコード内の入れ子のinclude 3つで3
include:spf.crm.example.org2include自体で1、その中のa:で1
include:news.example.net3include自体で1、入れ子のincludeで1、その中のexists:で1
a1旧Webサーバー用の消し忘れ
-all0問い合わせなし
合計11上限を1つ超過

この合計は最悪の場合の数です。最初のincludeで一致する送信元なら、問い合わせは多くても5回で済み、passします。前の項目のどれにも一致しない送信元は最後のa、つまり11回目の問い合わせまで進むので、なりすましメールは-allによる本来のfailではなくpermerrorになります。送信サービスを追加したときはもちろん、それ以外でも定期的に数え直してください。includeの参照先は予告なく変わります。

メールを失わずに10回以内へ戻す方法

  • 使っていないincludeを削除する。以前のメールサービス、解約したメルマガ配信ツール、数週間だけ試したCRMなどです。DMARC集計レポートを見れば、どの送信元が実際にそのドメイン名で送信しているかが分かり、誰も使っていないincludeを見つける手がかりになります。削除する前に、各サービスの担当者に確認してください。
  • そのincludeが評価されているかを確かめる。SPFはエンベロープ送信者のドメインに対して確認されます。あるサービスのメッセージのReturn-Pathにそのサービス自身のドメインが入っているなら、受信側が確認するのはそのドメインのSPFレコードで、あなたのレコードではありません。その場合、あなたのincludeはそのメールに何の役割も果たしていません。外す前にプロバイダーのドキュメントで確認してください。
  • 自社管理のサーバーはaやmxではなくip4・ip6で書く。自社サーバーが192.0.2.10から送信しているなら、ip4:192.0.2.10は1回も消費しませんが、aは1回消費します。mxがそもそも必要かも見直しましょう。メールを受信するサーバーが送信もするとは限らず、それがメールサービスのサーバーなら、そのサービスのincludeがすでに送信サーバーを含んでいることもあります。サーバーのアドレスが変わったら、記載も更新してください。
  • 大量送信するサービスごとにサブドメインを分ける。メルマガ配信やサポート窓口のサービスには、news.example.comのようなバウンス用ドメインをエンベロープ送信者として使ってもらい、専用のSPFレコードと専用の10回枠を持たせます。SPFは継承されないので、サブドメインにはそのレコードが必要です。DMARCの既定である緩やかな整合(relaxed)なら、news.example.comはexample.comのFromアドレスと整合したままです。多くのサービスでは「カスタムReturn-Path」や「独自バウンスドメイン」と呼ばれる設定です。
  • ptrをやめる。RFC 7208は公開すべきでないとしています。遅く、接続元IPアドレスの所有者が管理する逆引きDNSに依存し、しかもルックアップを消費します。ip4・ip6かプロバイダーのincludeに置き換えてください。
  • 並べ替えは解決にならない。送信量の多いサービスを先頭に移せば、11回目に達するメッセージは減りますが、後ろに続くものについてはレコードが壊れたままです。本来-allで弾かれるべきなりすましメールも例外ではありません。

DKIMも負担を和らげます。DMARCは整合した合格が一つあれば足り、SPFと違ってDKIMは転送されても多くの場合そのまま有効です。すべてのサービスが自社ドメインで署名していることを確認してから、SPFレコードを整理しましょう。

SPFのフラット化:ルックアップは減るが手間は増える

フラット化(flattening)は、includeを、その時点で解決されるip4・ip6のアドレス範囲に置き換える方法です。ルックアップ回数は減りますが、あなたのレコードは他社のリストの写しを抱えることになり、その写しは自動では更新されません。

リスク起きること抑え方
プロバイダーが範囲を追加・変更する新しいアドレスから送られたメールがSPFでfailになり、誰も知らせてくれない安定した範囲を文書で公開しているプロバイダーに限ってフラット化し、定期的に照合する
プロバイダーが範囲を使わなくなる手放されたアドレスは、後で別の誰かが使うかもしれないのに、あなたのレコードでは許可されたまま残るプロバイダーが公開しなくなった範囲を削除する
レコードが長くなるRFC 7208はSPFの応答を512オクテット以内に収めるよう勧めている。1つのUDPパケットに収まらない応答は、ファイアウォールがTCPでのDNSやEDNS0を妨げる環境では黙って無視されることがある消費が特に大きい少数のincludeだけをフラット化する
インフラが頻繁に変わる大規模なクラウドサービスは送信アドレスを入れ替えるそのincludeはそのまま残す。たとえばMicrosoftは、Microsoft 365のincludeをフラット化しないよう案内している

自動フラット化サービスは、includeを定期的に解決し直してレコードを書き換えます。範囲が古くなる問題には対処できますが、自社のDNSに第三者が関わることになり、更新に失敗すればSPFが壊れます。まずは他の方法を試してください。それでもフラット化する場合は、どのincludeをいつ置き換えたかを記録し、プロバイダーが公開している範囲と定期的に照合し、変更のたびにSPFを確認してください(Microsoft Learnも自社の利用者に同じことを勧めています)。

1つの名前にSPFレコードは1つ、長い場合は正しく分割する

1つの名前に置ける、v=spf1で始まるTXTレコードはちょうど1つです。2つあると受信側はどちらを使うか決められず、permerrorを返します(RFC 7208の4.5節)。新しいサービスの設定手順に「このTXTレコードを追加してください」とあり、既存のレコードを編集せずに2つ目のSPFレコードを作ってしまう、というのがよくある原因です。レコードを2つにしても上限は倍になりません。1つにまとめてください。

# 誤り:同じ名前にSPFレコードが2つ
example.com.  TXT  "v=spf1 include:_spf.mail.example.net -all"
example.com.  TXT  "v=spf1 include:spf.crm.example.org ~all"

# 正しい:1つのレコード
example.com.  TXT  "v=spf1 include:_spf.mail.example.net include:spf.crm.example.org -all"

同じ名前にある他のTXTレコード、たとえばサービスのドメイン確認用トークンは問題ありません。数えられるのはv=spf1で始まるレコードだけです。TXTレコード内の1つの文字列は最大255文字です。それより長いSPFレコードも1つのTXTレコードのままで、引用符で囲んだ複数の文字列に分けて書きます。受信側はそれらを空白を入れずに連結する(3.3節)ので、項目の区切りにあたる位置では、文字列の末尾に空白を入れておきます。

example.com.  TXT  "v=spf1 ip4:192.0.2.0/24 ip6:2001:db8::/32 include:_spf.mail.example.net " "include:spf.crm.example.org -all"

同じpermerrorは、ほかの間違いでも起きます。include:をinclude=と書くなど、レコードのどこかに構文エラーがある場合(4.6節)や、SPFレコードのないドメインをincludeしている場合です。また、SPFレコードは継承されません。エンベロープ送信者として使う名前ごとに専用のレコードが必要で、それぞれに10回の上限があります。

無料のSPF・DMARCチェッカーが数えるもの

Sitelemetryの無料の公開準備スナップショットは、登録なしで1つの公開ドメインに8項目のパッシブなチェックを行い、そのうち一つがメール関連のDNSレコードを読みます。ペネトレーションテストではなく、6領域の監査でもありません。メールを送信することもありません。

  • 入力したホスト名そのもの。入力した名前のSPFレコードを読み、親ドメインにはさかのぼりません。エンベロープ送信者のドメイン自体を入力してください。たとえばwww.example.comではなくexample.comです。
  • 最上位レコードだけを数える。そのレコードに書かれたDNS問い合わせを伴う項目(include、a、mx、ptr、exists、redirect)を数え、10個を超えると「中」のリスクシグナルとして示します。
  • たどらないもの。include:やredirect=の参照先はたどらず、voidルックアップも数えず、同じ名前にある2つ目のSPFレコードも指摘しません。上の5項目の例は、受信側では11回のルックアップが必要なのに、この数え方では上限内と判定されます。再帰的な数え直しはご自身でも行ってください。
  • SPFのその他のシグナル。+allは「高」、?allは「低」のリスクとして示されます。ホストにMXレコードがあるのにSPFやDMARCのレコードがない場合も指摘されます。

メールDNSのチェックを最初に開きたいときは、無料のSPF・DMARCチェッカーを使ってください。同じスナップショットを実行し、そのチェックを他の7つより上に表示します。~allと-allの選び方や、DMARCをnoneからrejectへ進める手順など、レコードのその他の部分はSPF・DMARCの確認方法の解説で扱っています。

よくある質問

「SPF permerror: too many DNS lookups」とはどういう意味ですか?

SPFレコードの評価に、includeの参照先も含めて10個を超えるDNS問い合わせを伴う項目が必要になった、という意味です。RFC 7208は、その時点で受信側が処理を止めてpermerrorを返すよう定めているため、SPFはDMARCの合格に役立たなくなります。

ip4やip6はSPFのルックアップ上限に数えますか?

数えません。ip4、ip6、allはDNS問い合わせが不要なので対象外です。include、a、mx、ptr、exists、redirectはそれぞれ1回と数え、includeとredirectは参照先レコード内の問い合わせもすべて加算します。

SPFの上限は「includeが10個」ですか、「ルックアップが10回」ですか?

ルックアップが10回です。includeが4つしかないレコードでも、参照先のレコードがさらにincludeやa、mxを含んでいれば上限を超えることがあります。見えている項目だけでなく、includeとredirectをすべて再帰的にたどって数えてください。

SPFレコードを2つ公開すれば、使えるルックアップ回数は増えますか?

増えません。同じ名前にv=spf1で始まるTXTレコードが2つあると、結果はpermerrorになります。1つのレコードにまとめるか、送信元を専用のサブドメインに移してください。サブドメインには独自のレコードと独自の10回枠があります。

SPFのフラット化で10回制限は解決しますか?

回数は減りますが、写したIPアドレス範囲はプロバイダーが変更すると古くなり、新しいアドレスから送られた正当なメールがSPFでfailになります。まず不要なincludeの削除とサブドメインの活用を検討し、フラット化は安定した範囲を文書で公開しているプロバイダーに限って、定期的に照合しながら使ってください。

SPFがpermerrorになると、DMARCも不合格になりますか?

必ずしもそうではありません。DMARCは整合した合格が一つあれば足りるので、Fromドメインと整合した有効なDKIM署名があるメールは合格します。SPFだけに頼っていたメールは合格しなくなるため、レコードを直す価値があります。

出典・参考資料

  1. RFC 7208:Sender Policy Framework(SPF)www.rfc-editor.org
  2. RFC 7208 4.6.4節:DNSルックアップの上限www.rfc-editor.org
  3. RFC 9989:DMARCwww.rfc-editor.org
  4. RFC 8601:メッセージの認証結果を示すヘッダーフィールドwww.rfc-editor.org
  5. Microsoft Learn:SPFを設定してMicrosoft 365ドメインの有効なメール送信元を識別するlearn.microsoft.com
Sitelemetryチーム

Sitelemetry編集チームが作成しています。詳しく学ぶには、掲載した参考資料をご覧ください。

SITELEMETRY

学んだことを実践へ。

Sitelemetryでサイトの構造、設定、技術的な手掛かりを探りましょう。