メインコンテンツまでスキップ

RFC 7208 - Sender Policy Framework (SPF)

  • ステータス: Proposed Standard
  • 発行日: April 2014
  • ストリーム: IETF
  • 廃止: RFC4408
  • エラッタ: エラッタなし

基本情報​

  • RFC番号: 7208
  • タイトル: Sender Policy Framework (SPF) for Authorizing Use of Domains in Email
  • 日本語タイトル: 送信者ポリシーフレームワーク
  • 公開日: 2014年4月
  • ステータス: PROPOSED STANDARD (提案標準)
  • 著者: S. Kitterman

概要 (Abstract)​

SPFは、ドメイン所有者がDNSレコードを通じて、そのドメインのメール送信を許可されたメールサーバーを指定できるようにします。受信者はSPFレコードを照会して、メールが許可されたサーバーから送信されたかを検証でき、メールスプーフィングの検出とブロックに役立ちます。

Contents​

Appendices (付録)​

SPF概要​

SPFとは?​

定義:

SPF = Sender Policy Framework (送信者ポリシーフレームワーク)
機能: 認可されたメール送信サーバーの検証
方法: DNS TXTレコード
目的:
✓ メールスプーフィングの防止
✓ スパムの削減
✓ メール配信性の向上

メールセキュリティトリオ:
1. SPF (このRFC) - 送信サーバーを検証
2. DKIM (RFC 6376) - メールコンテンツを検証
3. DMARC (RFC 7489) - 統一ポリシーとレポート

動作原理:

送信者 (example.com):
1. DNSにSPFレコードを公開
example.com. IN TXT "v=spf1 ip4:203.0.113.1 -all"
→ 203.0.113.1のみ認可

2. メールサーバーが通常どおりメールを送信
MAIL FROM: `<[email protected]>`

受信者:
1. 送信者ドメインを抽出
MAIL FROM: [email protected] → ドメイン: example.com

2. SPFレコードを照会
DNSクエリ: example.com TXTレコード

3. 送信サーバーIPを確認
送信サーバーIP: 203.0.113.1
SPFレコード許可: ip4:203.0.113.1
→ 一致!

4. SPF結果:
Pass ✓ → 認可されたサーバー
Fail ✗ → 認可されていないサーバー

SPF vs DKIM vs DMARC​

機能比較:

SPF (RFC 7208):
- 検証対象: 送信サーバーIP
- 位置: SMTP MAIL FROM
- DNSレコード: TXT
- 制限: 転送メールは失敗する

DKIM (RFC 6376):
- 検証対象: メールデジタル署名
- 位置: DKIM-Signatureヘッダー
- DNSレコード: TXT (_domainkey)
- 制限: 正しい鍵設定が必要

DMARC (RFC 7489):
- 検証対象: SPF + DKIMアライメント
- 位置: Fromヘッダー
- DNSレコード: TXT (_dmarc)
- 機能: ポリシー + レポート

組み合わせ使用:
SPF + DKIM → DMARC合格 → 最適な保護

SPFレコード形式​

基本構文​

v=spf1 <mechanisms> <qualifiers> <modifiers>

例:
v=spf1 ip4:192.0.2.0/24 include:_spf.example.com -all

コンポーネント:
- v=spf1: バージョン識別子 (必須、常にspf1)
- mechanisms: マッチングメカニズム
- qualifiers: 結果修飾子
- modifiers: 修飾子

メカニズム (Mechanisms)​

1. all:

定義: すべてのIPにマッチ
使用: 通常は最後のデフォルトポリシーとして

例:
v=spf1 -all すべてのIP不許可 (最も厳格)
v=spf1 ~all すべてのIPソフト失敗 (推奨)
v=spf1 +all すべてのIP許可 (非推奨!)

2. ip4/ip6:

定義: 明示的なIPアドレスまたは範囲

例:
v=spf1 ip4:203.0.113.1 -all
→ 203.0.113.1のみ許可

v=spf1 ip4:192.0.2.0/24 -all
→ 192.0.2.0-192.0.2.255を許可

v=spf1 ip6:2001:db8::1 -all
→ IPv6アドレスを許可

v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.0/24 -all
→ 複数の範囲を許可

3. a:

定義: 現在のドメインのA/AAAAレコード

例:
v=spf1 a -all
→ example.comのAレコードからのIPを許可

v=spf1 a:mail.example.com -all
→ mail.example.comのAレコードからのIPを許可

v=spf1 a/24 -all
→ example.comのAレコードIPの/24ネットワークを許可

4. mx:

定義: 現在のドメインのMXレコード

例:
v=spf1 mx -all
→ example.comのMXサーバーIPを許可

v=spf1 mx:example.com -all
→ example.comのMXサーバーIPを許可

v=spf1 mx/24 -all
→ MXサーバーの/24ネットワークを許可

5. include:

定義: 別のドメインのSPFレコードを含める

例:
v=spf1 include:_spf.google.com -all
→ Googleメールサーバーを許可 (Gmail for Business)

v=spf1 include:spf.protection.outlook.com -all
→ Microsoft 365メールサーバーを許可

複数のinclude:
v=spf1 include:_spf.google.com include:spf.protection.outlook.com -all

注意: 最大10 DNS検索制限!

6. exists:

定義: 指定されたドメインにAレコードがある場合にマッチ

例:
v=spf1 exists:%\{i}.spamhaus.example.com -all
→ 高度な使用、通常はスパムブラックリストチェック用

マクロ展開:
%\{i} = 送信サーバーIP (逆順)

7. ptr (非推奨):

定義: 逆引きDNSクエリ

例:
v=spf1 ptr:example.com -all

問題点:
❌ パフォーマンスが悪い (逆引きDNSクエリが必要)
❌ 信頼性が低い
❌ RFC明示的に使用非推奨

代替: ip4/ip6またはincludeを使用

修飾子 (Qualifiers)​

シンボル | 名前 | 意味 | 推奨使用
---------|------|------|----------
+ | Pass | 合格 (デフォルト) | 認可されたサーバー
- | Fail | 失敗 | メール拒否
~ | SoftFail | ソフト失敗 | 受け入れるがマーク
? | Neutral | 中立 | 明確なポリシーなし

例:
v=spf1 +ip4:203.0.113.1 -all
↑明示的pass ↑明示的fail

v=spf1 ip4:203.0.113.1 ~all
↑デフォルト+ ↑softfail

v=spf1 ?all
↑neutral (SPFなしと同等)

修飾子使用推奨:

+ (Pass): 
✓ 認可されたメールサーバー
例: +ip4:203.0.113.1

- (Fail):
✓ 最終的な-all (厳格)
✓ 明示的に特定のIPを禁止
例: -all

~ (SoftFail):
✓ 最終的な~all (緩やか、初期推奨)
✓ 移行期間に使用
例: ~all

? (Neutral):
✗ まれに使用
✗ ポリシーなしと同等

修飾子 (Modifiers)​

1. redirect:

定義: 別のドメインのSPFレコードにリダイレクト

例:
example.com: v=spf1 redirect=_spf.example.com
_spf.example.com: v=spf1 ip4:203.0.113.1 -all

目的:
✓ 集中的なSPF管理
✓ 複数のドメインがポリシーを共有

注意:
- redirectの後に他のメカニズムは続けられない
- allと一緒に使用できない

2. exp:

定義: 説明 (SPF失敗時の説明テキスト)

例:
v=spf1 -all exp=explain.example.com

explain.example.com TXTレコード:
"This domain does not send email"

目的:
✓ ユーザーフレンドリーなエラーメッセージを提供
- 実際にはまれに使用される

SPFレコード例​

基本設定​

1. 単一メールサーバー:

v=spf1 ip4:203.0.113.1 -all

説明:
- 203.0.113.1のみメール送信可能
- 他のIPは拒否される

2. MXレコードの使用:

v=spf1 mx -all

説明:
- ドメインのMXサーバーがメール送信を許可
- MX変更に自動的に適応

3. 複数のIP範囲:

v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24 -all

説明:
- 2つのクラスCネットワークを許可
- マルチデータセンター展開に適している

サードパーティサービス​

4. Google Workspace (Gmail for Business):

v=spf1 include:_spf.google.com -all

説明:
- Googleを使用してメール送信
- GoogleのSPFレコードを含める

5. Microsoft 365:

v=spf1 include:spf.protection.outlook.com -all

6. SendGrid:

v=spf1 include:sendgrid.net -all

7. Mailchimp:

v=spf1 include:servers.mcsv.net -all

混合設定​

8. 自前サーバー + サードパーティ:

v=spf1 ip4:203.0.113.1 include:_spf.google.com -all

説明:
- 自前サーバー: 203.0.113.1
- Google Workspace: include

9. 複数のサードパーティサービス:

v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:sendgrid.net -all

注意: 各includeがDNS検索としてカウントされる

10. メールを送信しないドメイン:

v=spf1 -all

説明:
- ドメインはメールを送信しない
- スプーフィングを防止
- 受信専用ドメインに適している

サブドメイン​

11. サブドメインの個別設定:

example.com: v=spf1 ip4:203.0.113.1 -all
mail.example.com: v=spf1 include:_spf.google.com -all

説明:
- メインドメインは自前サーバーを使用
- mailサブドメインはGoogleを使用

12. サブドメインの継承 (SPFレコードなしの場合):

mail.example.comにSPFレコードがない場合:
→ example.comのSPFレコードを使用

継承を望まない場合:
mail.example.com: v=spf1 -all

SPF検証フロー​

受信者検証ステップ​

// SPF検証擬似コード
async function checkSPF(clientIP, sender, helo) {
// 1. ドメインを抽出
const domain = sender.split('@')[1]; // [email protected] → example.com

// 2. SPFレコードを照会
const spfRecord = await queryDNS(domain, 'TXT', 'v=spf1');

if (!spfRecord) {
return 'none'; // SPFレコードなし
}

// 3. SPFレコードを解析
const mechanisms = parseSPF(spfRecord);

// 4. メカニズムを順番にチェック
for (const mechanism of mechanisms) {
const result = await evaluateMechanism(mechanism, clientIP, domain);

if (result !== null) {
return result; // マッチが見つかった、結果を返す
}
}

return 'neutral'; // マッチなし
}

// 単一メカニズムの評価
async function evaluateMechanism(mechanism, clientIP, domain) {
const { type, value, qualifier } = mechanism;

switch (type) {
case 'ip4':
if (isInIPRange(clientIP, value)) {
return mapQualifier(qualifier); // +pass, -fail, ~softfail
}
break;

case 'a':
const aRecords = await queryDNS(value || domain, 'A');
if (aRecords.includes(clientIP)) {
return mapQualifier(qualifier);
}
break;

case 'mx':
const mxRecords = await queryDNS(value || domain, 'MX');
for (const mx of mxRecords) {
const mxIPs = await queryDNS(mx, 'A');
if (mxIPs.includes(clientIP)) {
return mapQualifier(qualifier);
}
}
break;

case 'include':
const includeResult = await checkSPF(clientIP, `user@${value}`, null);
if (includeResult === 'pass') {
return mapQualifier(qualifier);
}
break;

case 'all':
return mapQualifier(qualifier);
}

return null; // マッチなし
}

SPF結果​

戻り値         | 意味              | 推奨処理
---------------|-------------------|------------------
none | SPFレコードなし | 受け入れる (信頼度低)
neutral | 明示的にポリシー | 受け入れる
| なし |
pass | 合格 | 受け入れる
fail | 失敗 | 拒否する
softfail | ソフト失敗 | 受け入れるがマーク
temperror | 一時エラー | 後で再試行
permerror | 永続エラー | 拒否する

SMTP応答例:
pass: 250 OK (SPF pass)
fail: 550 SPF check failed
softfail: 250 OK (X-SPF: softfailヘッダー追加)

DNS検索制限​

10検索制限​

問題:

SPF検証は最大10回のDNS検索を実行
制限超過 → permerror (永続エラー)

10検索にカウントされる:
✓ include
✓ a
✓ mx
✓ exists
✓ redirect

カウントされない:
✗ ip4/ip6 (直接マッチング)
✗ all (直接マッチング)

例 - 制限超過:

v=spf1 
include:_spf1.example.com ← 1
include:_spf2.example.com ← 2
include:_spf3.example.com ← 3
include:_spf4.example.com ← 4
include:_spf5.example.com ← 5
include:_spf6.example.com ← 6
include:_spf7.example.com ← 7
include:_spf8.example.com ← 8
include:_spf9.example.com ← 9
include:_spf10.example.com ← 10
include:_spf11.example.com ← 超過! permerror
-all

includeがさらにincludeを含む場合、それらもカウントされる!

解決策:

1. a/mxの代わりにip4/ip6を使用
❌ v=spf1 a mx -all (2検索)
✓ v=spf1 ip4:203.0.113.1 ip4:198.51.100.1 -all (0検索)

2. includeをマージ
❌ include:service1.com include:service2.com
✓ すべてのIPを含む独自のSPFレコードを維持

3. SPF Flattening (SPF平坦化)
定期的にincludeからIPを照会し、ip4/ip6に変換

SPF Flatteningツール​

// SPF Flattening例
async function flattenSPF(domain) {
const spf = await querySPF(domain);
const ips = [];

// SPFを解析
const mechanisms = parseSPF(spf);

for (const mech of mechanisms) {
if (mech.type === 'ip4' || mech.type === 'ip6') {
ips.push(mech.value);
} else if (mech.type === 'include') {
// 再帰的にincludeを照会
const includeIPs = await resolveInclude(mech.value);
ips.push(...includeIPs);
} else if (mech.type === 'a') {
const aRecords = await queryDNS(mech.value, 'A');
ips.push(...aRecords.map(ip => `ip4:${ip}`));
} else if (mech.type === 'mx') {
const mxRecords = await queryDNS(mech.value, 'MX');
for (const mx of mxRecords) {
const mxIPs = await queryDNS(mx, 'A');
ips.push(...mxIPs.map(ip => `ip4:${ip}`));
}
}
}

// 平坦化されたSPFを生成
return `v=spf1 ${ips.join(' ')} -all`;
}

// 使用
const flatSPF = await flattenSPF('example.com');
console.log('Flattened SPF:', flatSPF);
// v=spf1 ip4:74.125.0.0/16 ip4:209.85.128.0/17 ip4:216.58.192.0/19 -all

実用ツール​

SPFレコードジェネレーター​

class SPFBuilder {
constructor(domain) {
this.domain = domain;
this.mechanisms = [];
this.modifier = null;
}

addIP(ip) {
if (ip.includes(':')) {
this.mechanisms.push(`ip6:${ip}`);
} else {
this.mechanisms.push(`ip4:${ip}`);
}
return this;
}

addIPRange(cidr) {
if (cidr.includes(':')) {
this.mechanisms.push(`ip6:${cidr}`);
} else {
this.mechanisms.push(`ip4:${cidr}`);
}
return this;
}

useA() {
this.mechanisms.push('a');
return this;
}

useMX() {
this.mechanisms.push('mx');
return this;
}

include(domain) {
this.mechanisms.push(`include:${domain}`);
return this;
}

setDefault(qualifier) {
const qualifiers = { pass: '+all', fail: '-all', softfail: '~all', neutral: '?all' };
this.mechanisms.push(qualifiers[qualifier] || '-all');
return this;
}

redirect(domain) {
this.modifier = `redirect=${domain}`;
return this;
}

build() {
let spf = 'v=spf1';

if (this.mechanisms.length > 0) {
spf += ' ' + this.mechanisms.join(' ');
}

if (this.modifier) {
spf += ' ' + this.modifier;
}

return spf;
}

countLookups() {
let count = 0;
for (const mech of this.mechanisms) {
if (mech.startsWith('include:') || mech.startsWith('a') ||
mech.startsWith('mx') || mech.startsWith('exists:')) {
count++;
}
}
if (this.modifier && this.modifier.startsWith('redirect=')) {
count++;
}
return count;
}
}

// 使用例
const spf = new SPFBuilder('example.com')
.addIP('203.0.113.1')
.addIPRange('192.0.2.0/24')
.include('_spf.google.com')
.include('spf.protection.outlook.com')
.setDefault('fail')
.build();

console.log('SPF Record:', spf);
console.log('DNS Lookups:', spf.countLookups());

// 出力:
// SPF Record: v=spf1 ip4:203.0.113.1 ip4:192.0.2.0/24 include:_spf.google.com include:spf.protection.outlook.com -all
// DNS Lookups: 2

SPF検証ツール​

const dns = require('dns').promises;

class SPFChecker {
async check(domain, ip) {
try {
// SPFレコードを照会
const records = await dns.resolveTxt(domain);
const spfRecord = records.find(r =>
r.join('').startsWith('v=spf1')
);

if (!spfRecord) {
return { result: 'none', message: 'No SPF record found' };
}

const spf = spfRecord.join('');
console.log('SPF Record:', spf);

// 解析して検証
const result = await this.evaluate(spf, ip, domain);

return result;

} catch (err) {
return { result: 'temperror', message: err.message };
}
}

async evaluate(spf, ip, domain, depth = 0) {
if (depth > 10) {
return { result: 'permerror', message: 'Too many DNS lookups' };
}

const parts = spf.split(/\s+/);

for (const part of parts) {
if (part === 'v=spf1') continue;

// 修飾子を抽出
let qualifier = '+';
let mechanism = part;

if (['+', '-', '~', '?'].includes(part[0])) {
qualifier = part[0];
mechanism = part.slice(1);
}

// メカニズムをチェック
if (mechanism.startsWith('ip4:')) {
const range = mechanism.slice(4);
if (this.isIPInRange(ip, range)) {
return this.mapResult(qualifier);
}
} else if (mechanism.startsWith('include:')) {
const includeDomain = mechanism.slice(8);
const includeRecords = await dns.resolveTxt(includeDomain);
const includeSPF = includeRecords.find(r =>
r.join('').startsWith('v=spf1')
);

if (includeSPF) {
const result = await this.evaluate(
includeSPF.join(''),
ip,
includeDomain,
depth + 1
);

if (result.result === 'pass') {
return this.mapResult(qualifier);
}
}
} else if (mechanism === 'all' || mechanism === '-all' ||
mechanism === '~all' || mechanism === '?all') {
return this.mapResult(qualifier);
}
// 他のメカニズムをここに追加可能...
}

return { result: 'neutral', message: 'No match found' };
}

isIPInRange(ip, range) {
// 簡易版、本番環境では完全な実装が必要
if (!range.includes('/')) {
return ip === range;
}
// CIDRマッチング実装は省略...
return false;
}

mapResult(qualifier) {
const map = {
'+': { result: 'pass', message: 'SPF pass' },
'-': { result: 'fail', message: 'SPF fail' },
'~': { result: 'softfail', message: 'SPF softfail' },
'?': { result: 'neutral', message: 'SPF neutral' }
};
return map[qualifier] || map['+'];
}
}

// 使用
const checker = new SPFChecker();
const result = await checker.check('example.com', '203.0.113.1');
console.log('SPF Check Result:', result);

デプロイのベストプラクティス​

1. 段階的デプロイ​

フェーズ1: 監視モード
v=spf1 ?all
または
v=spf1 ~all

目的: データを収集し、どのサーバーがメールを送信しているか観察
期間: 2-4週間

フェーズ2: ソフト失敗
v=spf1 ip4:x.x.x.x include:provider.com ~all

目的: 認可されていないメールをマークするが拒否しない
期間: 4-8週間

フェーズ3: 厳格モード
v=spf1 ip4:x.x.x.x include:provider.com -all

目的: 認可されていないメールを拒否

2. よくある間違い​

❌ 間違い1: -allを忘れる
v=spf1 ip4:203.0.113.1
→ v=spf1 ip4:203.0.113.1 ?all と同等
→ どのIPもneutral

✓ 正しい:
v=spf1 ip4:203.0.113.1 -all

❌ 間違い2: 複数のSPFレコード
example.com TXT "v=spf1 ip4:203.0.113.1 -all"
example.com TXT "v=spf1 include:provider.com -all"
→ permerror

✓ 正しい: 1つにマージ
v=spf1 ip4:203.0.113.1 include:provider.com -all

❌ 間違い3: 10検索以上
v=spf1 include:a include:b include:c ... (多すぎる)

✓ 正しい: ip4を直接使用またはflattening

❌ 間違い4: ptrの使用
v=spf1 ptr:example.com -all
→ パフォーマンスが悪い、非推奨

✓ 正しい: ip4またはincludeを使用

3. テストと検証​

# コマンドラインテストツール

# 1. SPFレコードを照会
dig example.com TXT | grep "v=spf1"
または
nslookup -type=TXT example.com

# 2. オンラインツールを使用
# - https://mxtoolbox.com/spf.aspx
# - https://www.kitterman.com/spf/validate.html

# 3. テストメールを送信
# 自分のメールアドレスに送信し、ヘッダーを確認:
# Received-SPF: pass ...

DKIM/DMARCとの統合​

完全なメールセキュリティ設定​

1. SPFレコード:
example.com. IN TXT "v=spf1 ip4:203.0.113.1 include:_spf.google.com -all"

2. DKIMレコード:
default._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIGfMA0..."

3. DMARCレコード:
_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:[email protected]"

結果:
- SPFが送信サーバーを検証 ✓
- DKIMがメールコンテンツを検証 ✓
- DMARCがポリシーを統一 ✓
→ 三重保護!

参考資料​

SPF関連RFC:

  • [RFC 7208] SPF ← この文書
  • [RFC 7489] DMARC
  • [RFC 6376] DKIM

関連リソース:


まとめ: SPFはメールセキュリティの最前線です。DNSレコードを通じて送信サーバーが認可され、メールスプーフィングが効果的に防止されます。DKIMとDMARCと組み合わせることで、完全なメールセキュリティシステムが構築されます。忘れないでください: ソフト失敗から始めて、徐々に厳格モードに強化し、10 DNS検索制限に注意してください!


1. Introduction (導入)​

現在の電子メールインフラストラクチャは、システムにメールを投入するどのホストも、[RFC5321]および[RFC5322]で指定されているさまざまな識別子で任意のDNSドメイン名を使用できるという特性を持っています。この特性はいくつかのケースで有利ですが、未承諾の大量電子メール(UBE, Unsolicited Bulk Email、スパムとしても知られる)を削減する上で主要な障害となっています。さらに、ADMD([RFC5598]で説明されている)は、他のエンティティが自分のドメイン名を容易に使用できること、しばしば悪意を持って使用されることを当然懸念しています。

このドキュメントは、ADMDが「MAIL FROM」または「HELO」アイデンティティで自分のドメイン名を使用することをホストに認可できるプロトコルを定義します。準拠したADMDは、どのホストが自分の名前を使用することを許可されているかを指定するSender Policy Framework (SPF)レコードをDNSで公開し、準拠したメール受信者は、公開されたSPFレコードを使用して、メールトランザクション中に特定の「HELO」または「MAIL FROM」アイデンティティを使用する送信メール転送エージェント(MTA, Mail Transfer Agent)の認可をテストします。

メール受信者にとってのもう1つの利点は、アイデンティティの使用を検証した後、ホストのIPアドレスではなく送信者のドメインに基づいてメールに関するローカルポリシー決定を行うことができることです。これは、ドメイン名の評判がホストIPアドレスの評判よりも正確である可能性があるため有利です。なぜなら、ドメイン名はより長い期間にわたってより安定している可能性があるためです。さらに、主張されたアイデンティティが検証できない場合、ローカルポリシーはそのような電子メールに対してより厳しい措置を取ることができます。例えば、それを拒否することができます。

1.1 Terminology (用語)​

1.1.1 Key Words (キーワード)​

このドキュメントのキーワード「MUST」、「MUST NOT」、「REQUIRED」、「SHALL」、「SHALL NOT」、「SHOULD」、「SHOULD NOT」、「RECOMMENDED」、「NOT RECOMMENDED」、「MAY」、および「OPTIONAL」は、[RFC2119]で説明されているように解釈されるべきです。

1.1.2 Imported Definitions (インポート定義)​

ABNF (Augmented Backus-Naur Form)は[RFC5234]で定義されており、トークン「ALPHA」、「DIGIT」、および「SP」(スペース)もそこで定義されています。

トークン「Local-part」、「Domain」、および「Mailbox」は[RFC5321]で定義されています。

「dot-atom」、「quoted-string」、「comment」、「CFWS」(Comment Folded White Space)、「FWS」(Folded White Space)、および「CRLF」(Carriage-Return/Line-Feed)は[RFC5322]で定義されています。

1.1.3 MAIL FROM Definition (MAIL FROM定義)​

このドキュメントは、[RFC5321]で説明されているメッセージ送信者のアイデンティティに関係しています:

トランザクションはMAILコマンドで始まり、これが送信者のアイデンティティを提供します。

このアイデンティティには多くの他の名前があるため、以下の条件を満たす名前を選択することが重要です:

  1. 一般的に使用されている

  2. 明確に定義されている

したがって、このドキュメントでは「MAIL FROM」という用語が使用され、これは[RFC5598]で説明されているRFC5321.MailFrom (Reverse-Path)アイデンティティとして定義されています。

1.1.4 HELO Definition (HELO定義)​

このドキュメントはHELO/EHLOアイデンティティも使用します。「HELO」アイデンティティはSMTP HELOまたはEHLOコマンドに由来します([RFC5321]参照)。HELOとEHLOは多くの場合に互換的に使用できるため、このドキュメントでは通常「HELO」として識別されます。これは[RFC5598]で定義されているRFC5321.HELO/.EHLOを意味します。これらのコマンドは、SMTPセッションのためにSMTPクライアント(送信ホスト)のアイデンティティを提供します。

1.2 check_host()​

セクション4では、着信電子メールトランザクションに対してSPFポリシーを評価するために使用されるアルゴリズムを紹介します。初期の実装では、このアルゴリズムはcheck_host()という名前の関数でコード化されていました。この名前は、このドキュメントでSPF評価アルゴリズムのシンボルとして使用されていますが、もちろん実装者はこの名前を使用する必要はありません。


2. Operational Overview (運用概要)​

2.1 Publishing Authorization (認可の公開)​

SPF準拠ドメインは、セクション3で説明されているように有効なSPFレコードを公開します。これらのレコードは、その中で指定されたMTAが関連するドメイン名を「HELO」および「MAIL FROM」アイデンティティで使用することを認可します。

SPF結果は、肯定的(ソースは認可されている)および否定的(ソースは認可されていない)な判定の両方を行うために使用できます。ADMDがSPFレコードを公開し、受信者が否定的な認可決定を行うことをサポートしたい場合、「-all」で終わるレコードを公開するか、そうする別のレコードにリダイレクトする必要があります。そうしないと、認可について決定的な判定を行うことはできません。セクション10では、否定的な決定に関連する潜在的な問題と軽減策について説明しています。

SMTPセッション中にHELOまたはMAIL FROMコマンドで自分のDNSドメイン名を使用することをホストに認可しないと宣言したいADMDは、メールアドレスのドメイン部分で使用されず、メールを送信することも期待されていないドメイン名に対して、そのようなSPFレコードを公開できます。

SPFレコードを変更する際は、すべての正当なメールが合理的に確認されたと予想されるまで、古いポリシーが有効なままである移行期間を確保するように注意する必要があります。[RFC5321]のセクション4.5.4.1では、メッセージが転送中にとどまる可能性のある時間について説明しています。オフライン検証は可能ですが、検証が元の送信時刻に近いほど、メッセージを送信した時点での送信ADMDの意図に一致するSPF結果を得る可能性が高くなります。

2.2 Checking Authorization (認可の確認)​

メール受信者は、受信した各メールに対してSPF検証のセットを実行できます。SPF検証は、与えられたアイデンティティでメールを送信するクライアントホストの認可をテストします。通常、そのような検証は受信MTAによって実行されますが、必要な情報が利用可能で信頼できる限り、メール処理チェーンの他の場所で実行することもできます。「MAIL FROM」および「HELO」アイデンティティは、それぞれセクション2.4および2.3に従って検証されます。

公開ADMDの明示的な承認なしに、SPFバージョン1レコードに対して他のアイデンティティを検証することは推奨されません。一部の状況では誤った結果が得られることが知られているためです。たとえば、ほとんどすべてのメーリングリストは「MAIL FROM」アイデンティティを書き換えますが(セクション10.3参照)、一部はメッセージ内の他のアイデンティティを変更しません。他のアイデンティティを定義するドキュメントは、明示的な承認方法を定義する必要があります。

メール受信者は、受信メールに対するより大きなテストセットの一部としてSPF検証を含めることができます。他のテストの結果は、特定のSPF検証が実行されるかどうかに影響を与える可能性があります。たとえば、ローカルホワイトリストで送信ホストのIPアドレスを見つけることは、他のすべてのテストをスキップし、そのホストからのすべてのメールを受け入れることにつながる可能性があります。

メール受信者がSPF検証を実行することを決定した場合、正しく実装されたcheck_host()関数(セクション4)を使用し、正しいパラメーターで評価する必要があります。テスト全体はオプションですが、実行することを決定した場合は、公開者と受信者の間で正しいセマンティクスを保持するために、指定されたとおりに実行する必要があります。

テストを実行するには、メール受信者はセクション4.1で説明されているパラメーターでcheck_host()関数を評価する必要があります。

無効、不正な形式、または存在しないドメインは、SPF検証が「none」を返す原因となります(SPFレコードが見つからないため)が、長い間、多くのMTAのポリシーは、特に無効な「MAIL FROM」の場合、そのようなドメインからのメールを拒否することでした。メールを拒否することは、SPFレコードを回避する1つの方法を防ぎます。

実装は、SMTP MAIL FROMコマンドで提供されたデータから<domain>を正しく抽出することに注意する必要があります。多くのMTAは、ソースルーティング([RFC5321]の付録C参照)、%-hack([RFC1123]参照)、およびバンパス([RFC1983]参照)などのものをまだ受け入れているためです。これらの古い機能は、セキュリティシステムを回避するために悪意を持って使用されてきました。

2.3 The "HELO" Identity ("HELO"アイデンティティ)​

SPF検証者は、「MAIL FROM」アイデンティティを検証するだけでなく、check_host()関数(セクション4)を「HELO」アイデンティティに<domain>として適用することによって、「HELO」アイデンティティを個別に検証することをお勧めします。「HELO」の検証は、一貫した結果を促進し、DNSリソースの使用を削減できます。「HELO」の検証に基づいてメッセージについて決定的な判定を行うことができる場合、通常より複雑な「MAIL FROM」を処理するためのDNSリソースを回避できます。さらに、「HELO」アイデンティティのために公開されたSPFレコードは単一のホストを指すため、利用可能な場合、ホスト認可ステータスの非常に信頼できるソースです。両方を検証する場合、最初に「HELO」を検証してから「MAIL FROM」を検証することをお勧めします。

送信者のためのEHLOまたはHELOコマンドで提示されるドメインの要件は常に明確ではなく、SPF検証者はアイデンティティがIPアドレスリテラルである([RFC5321]のセクション4.1.3参照)か、単に不正な形式であるかに備える必要があることに注意してください。このSPF検証は、「HELO」文字列が有効なマルチラベルドメイン名である場合にのみ実行できます。

2.4 The "MAIL FROM" Identity ("MAIL FROM"アイデンティティ)​

「HELO」検証が実行されなかったか、決定的なポリシー結果に達しなかった場合、SPF検証者は、check_host()関数を「MAIL FROM」アイデンティティに<domain>として適用することによって、「MAIL FROM」アイデンティティを検証する必要があります。

[RFC5321]は、リバースパスが空であることを許可しています([RFC5321]のセクション4.5.5参照)。この場合、明示的な送信者メールボックスはなく、そのようなメッセージはメールシステム自体からの通知メッセージであると想定できます。リバースパスが空の場合、このドキュメントは「MAIL FROM」アイデンティティを、ローカル部分「postmaster」と「HELO」アイデンティティ(以前に個別に検証されている場合もあれば、そうでない場合もあります)で構成されるメールボックスとして定義します。

2.5 Location of Checks (チェックの場所)​

認可チェックは、メールを受信するSMTPトランザクションの処理中に実行する必要があります。これにより、check_host()の入力として使用する正しいIPアドレスを決定する複雑さが軽減され、SMTP応答を介して送信MTAにエラーを直接返すことができます。[RFC7001]の付録Dは、このトピックについてより包括的な議論を提供しています。

認可チェックは、SMTPトランザクション中のMAILコマンドの時点で実行され、MAIL FROM値とクライアントIPアドレスを使用します。後の時点または他の入力で検証を実行すると、次の問題が発生する可能性があります:

  • 潜在的に偽装されたヘッダーから必要な情報を正確に抽出することが困難な場合があります。

  • 送信者のポリシーが変更されたため、正当なメールが認可検証に失敗する可能性があります。

認可検証に失敗した偽装されたアイデンティティに対して配信不能通知を生成することは、通常、バックスキャッター、つまり操作不可能な嫌がらせ拒否通知を構成します。オペレーターはそのような慣行を避けることを強くお勧めします。[RFC3834]のセクション2は、バックスキャッターとそれが引き起こす問題について説明しています。

2.6 Results of Evaluation (評価結果)​

セクション4は、上記で定義された入力とDNSで公開された送信者ポリシーを使用してクライアント認可に関する結論に到達するモデル関数定義であるcheck_host()を定義します。SPF検証者は、ここで定義された関数と意味的に同等なものを実装します。

このセクションでは、この関数の可能な出力をリストし、簡単に定義します。ただし、プロトコルは特定の結果の処理に対する規範的な要件を確立していないことに注意してください。各結果の処理オプションについては、セクション8で説明しています。

2.6.1 None​

「none」の結果は、(a)認可する<domain>として使用できる構文的に有効なDNSドメイン名がSMTPセッションから抽出できなかった、または(b)DNSからSPFレコードが取得されなかったことを意味します。

2.6.2 Neutral (中立)​

「neutral」結果は、ADMDがIPアドレスが認可されているかどうかを主張しないことを明示的に宣言したことを意味します。

2.6.3 Pass (合格)​

「pass」結果は、クライアントが与えられたアイデンティティでメールを注入することを認可されているという明示的な宣言です。

2.6.4 Fail (失敗)​

「fail」結果は、クライアントが与えられたアイデンティティでドメインを使用することを認可されていないという明示的な宣言です。

2.6.5 Softfail (ソフト失敗)​

「softfail」結果は、公開ADMDの弱い宣言であり、ホストが認可されていない可能性があることを示します。「fail」につながるより強力で明示的なポリシーを公開していません。

2.6.6 Temperror (一時エラー)​

「temperror」結果は、SPF検証者が検証の実行中に一時的な(通常はDNS)エラーに遭遇したことを意味します。後で再試行すると、DNSオペレーターのさらなる操作なしで成功する可能性があります。

2.6.7 Permerror (永続エラー)​

「permerror」結果は、ドメインの公開レコードが正しく解釈できなかったことを意味します。これは、解決するためにDNSオペレーターの介入が確実に必要なエラー状態を示しています。


3. SPF Records (SPFレコード)​

SPFレコードは、「HELO」および「MAIL FROM」アイデンティティでドメイン名を使用することが許可されている(および許可されていない)ホストを宣言するDNSレコードです。大まかに言えば、レコードはホストを許可されたセットと許可されていないセットに分割します(ただし、一部のホストはどちらのカテゴリにも属さない場合があります)。

SPFレコードは、単一のDNS TXTリソースレコードのRDATA内で見つかる単一のテキスト文字列として表されます。同じ所有者名を持つ複数のSPFレコードは許可されていません。レコード形式とレコード選択のプロセスは、以下のセクション4で説明されています。レコードの例は次のとおりです:

v=spf1 +mx a:colo.example.com/28 -all

このレコードはバージョン「spf1」で、3つのディレクティブを含んでいます: 「+mx」、「a:colo.example.com/28」(暗黙の「+」)、および「-all」。

各SPFレコードは、所有者名の下のサブドメインではなく、それが属する所有者名のDNSツリーに配置されます。これはSRVレコード[RFC2782]の慣行に似ています。

このセクションの例は、ドメインゾーンファイルの次の行で公開できます:

example.com. TXT "v=spf1 +mx a:colo.example.com/28 -all"

TXTレコードには複数の用途があるため、他の目的でそこに公開されている他のTXTレコードに注意してください。これらはサイズ制限の問題を引き起こす可能性があり(セクション3.4参照)、SPFレコードのみがSPF処理に使用されるようにすることに注意する必要があります。

SPFレコードを公開するADMDは、レコードを評価するために必要なDNS情報の量を最小限に抑える必要があります。セクション4.6.4およびセクション10.1.1は、「include」メカニズムとチェーンされた「redirect」修飾子に関するいくつかのアドバイスを提供しています。

3.1 DNS Resource Records (DNSリソースレコード)​

SPFレコードは、DNS TXT(タイプ16)リソースレコード(RR)[RFC1035]としてのみ公開する必要があります。レコードの文字内容は[US-ASCII]としてエンコードされます。SPF実験段階では、代替DNS RRタイプの使用がサポートされていましたが、これは現在廃止されています。

2003年、SPFが最初に開発されたとき、新しいDNS RRタイプを割り当てるための要件は現在よりも厳格でした。さらに、DNSサーバーおよび構成システムでの新しいDNS RRタイプの簡単な展開のサポートは広く展開されていませんでした。したがって、SPFの開発者は、SPFレコードを保存するためにTXT RRタイプを使用する方が簡単で実用的であることを発見しました。

[RFC4408]のレビューにおいて、SPFbisワーキンググループは、そのデュアルRRタイプ移行モデルは、実装者が提供および検証する必要のある普遍的なRRタイプを含んでいなかったため、根本的に欠陥があると結論付けました。この問題を解決するために多くの代替案が検討されましたが、最終的にワーキンググループは、予測可能な将来にSPF RRタイプへの移行の可能性は非常に低く、この相互運用性の問題に対する最良の解決策は、SPFバージョン1からSPF RRタイプのサポートを削除することであると結論付けました。詳細については、[RFC6686]の付録Aを参照してください。

10年前のSPFの初期展開を取り巻く状況は独特でした。既存のSPFレコードを再利用しないSPFアップデートが将来開発される場合、それはSPF RRタイプを使用できます。構造化データを格納するためのSPFによるTXT RRタイプの使用は、決して将来のプロトコル設計者の先例と見なされるべきではありません。新しいDNS RRタイプを使用する際の設計上の考慮事項に関するさらなる議論は、[RFC5507]で見つけることができます。

3.2 Multiple DNS Records (複数のDNSレコード)​

ドメイン名は、認可チェックが複数のレコードを選択することになる複数のレコードを持つことは決してありません。選択ルールについては、セクション4.5を参照してください。

3.3 Multiple Strings in a Single DNS Record (単一DNSレコード内の複数文字列)​

[RFC1035]のセクション3.3およびセクション3.3.14で定義されているように、単一のテキストDNSレコードは複数の文字列で構成できます。公開されたレコードに複数の文字列が含まれている場合、レコードはスペースを追加せずに連結されたこれらの文字列として扱われる必要があります。たとえば:

IN TXT "v=spf1 .... first" "second string..."

は以下と同等です:

IN TXT "v=spf1 .... firstsecond string..."

複数の文字列を含むTXTレコードは、単一のTXTレコード内の文字列の255オクテットの最大長を超えるレコードを構築するのに役立ちます。

3.4 Record Size (レコードサイズ)​

特定のドメイン名に対して公開されるSPFレコードは、そのクエリ結果が512オクテットに収まるように十分に小さく保つ必要があります。そうしないと、DNSプロトコルの制限を超える可能性があります。このUDP制限は[RFC1035]のセクション2.3.4で定義されていますが、[RFC2671]によって増加されています。512オクテット未満に保つことで、古いDNS実装がTCPにフォールバックするのを防ぎ、EDNS0 [RFC6891]サポートなしでUDPを使用できるようになります。応答サイズはこのドキュメントの範囲外の多くの事柄に依存するため、次のガイダンスのみを提供できます: DNSメッセージのサイズ、DNSネームの組み合わせの長さ、および特定のタイプのすべてのレコードのテキストが450オクテット未満の場合、DNS応答はUDPパケットに収まるはずです。TCP上のDNS操作またはEDNS0の使用を妨げるファイアウォールやその他の問題により、単一のUDPパケットに収まらないほど長いレコードは、SPF検証者によって黙って無視される可能性があります。

TXT形式クエリの応答サイズを計算する際には、ドメイン名で公開されている他のすべてのTXTレコードを考慮する必要があることに注意してください。同様に、SPF関連のすべてのクエリの応答サイズは、単一の512オクテットUDPパケットに収まるように評価する必要があります(つまり、DNSメッセージサイズは450オクテットに制限されます)。

3.5 Wildcard Records (ワイルドカードレコード)​

公開のためのワイルドカードレコードの使用は推奨されず、使用する場合は注意が必要です。ゾーンにワイルドカードMXレコードが含まれている場合、ワイルドカード宣言を公開したい場合がありますが、同じ要件と問題に従う必要があります。特に、任意のRRレコードを持つホストとそのサブドメインに対して、宣言を繰り返す必要があります。[RFC1034]のセクション4.3.3の例を考えてみましょう。これに基づいて、次のことができます:

EXAMPLE.COM. MX 10 A.EXAMPLE.COM
EXAMPLE.COM. TXT "v=spf1 a:A.EXAMPLE.COM -all"

*.EXAMPLE.COM. MX 10 A.EXAMPLE.COM
*.EXAMPLE.COM. TXT "v=spf1 a:A.EXAMPLE.COM -all"

A.EXAMPLE.COM. A 203.0.113.1
A.EXAMPLE.COM. MX 10 A.EXAMPLE.COM
A.EXAMPLE.COM. TXT "v=spf1 a:A.EXAMPLE.COM -all"

*.A.EXAMPLE.COM. MX 10 A.EXAMPLE.COM
*.A.EXAMPLE.COM. TXT "v=spf1 a:A.EXAMPLE.COM -all"

ゾーン内の各名前について、SPFレコードは2回リストする必要があります: その名前に対して1回、その名前の下のツリーをカバーするためにワイルドカードで1回、送信メールで使用されるすべてのドメインをカバーします。


4. The check_host() Function (check_host()関数)​

この説明は、アプリケーションプログラミングインターフェイスの定義ではなく、アルゴリズムを説明するための関数の説明です。適合するSPF実装は、この説明と意味的に同等の結果を生成する必要があります。

check_host()関数は、SPFレコードを取得し、解析し、評価して、特定のホストが特定のIDでメールを送信することが許可されているか許可されていないかを判断します。このチェックを実行する受信ADMDは、ここで説明されているようにcheck_host()関数を正しく評価する必要があります。

実装は、ここで定義されている正規のアルゴリズムとは異なるアルゴリズムを使用できますが、すべての場合において結果が同じである必要があります。

4.1 Arguments (引数)​

check_host()関数は、次の引数を取ります:

<ip> - メールを送信するSMTPクライアントのIPアドレス。IPv4またはIPv6。

<domain> - 求められている認証情報を提供するドメイン。最初は"MAIL FROM"または"HELO"IDのドメイン部分。

<sender> - "MAIL FROM"または"HELO"ID。

再帰的評価の場合、<sender>のドメイン部分は、check_host()が最初に評価されたときの<domain>引数とは異なる場合があります。ほとんどの場合、それは同じになります(以下のセクション5.2を参照)。セクション4.6.4で説明されているSPF用語の全体的なDNSクエリ制限は、単一の再帰的評価インスタンスだけでなく、すべての評価にわたる単一のグローバル制限として追跡する必要があります。

<domain>引数は、整形式のドメイン名ではない場合があることに注意してください。たとえば、リバースパスが空の場合、EHLO/HELOドメインとそれに関連する問題が使用されます(セクション2.3を参照)。これらの場合、check_host()はセクション4.3で"none"結果を返すように定義されています。

4.2 Results (結果)​

check_host()関数は、セクション2.6で説明されているいくつかの結果のいずれかを返すことができます。結果に基づいて実行するアクションは、受信者のローカルポリシーによって決定されます。これについてはセクション8で説明します。

4.3 Initial Processing (初期処理)​

<domain>が誤った形式である場合(たとえば、ラベルの長さが63文字を超える、非終端のゼロ長ラベルなど)、またはマルチラベルドメイン名でない場合、またはDNSクエリが"Name Error"(RCODE 3、"NXDOMAIN"とも呼ばれる[RFC2308])を返す場合、check_host()は直ちに"none"結果を返します。DNS RCODEは[RFC1035]で定義されています。整形式のドメインは、[RFC1983]で定義されている完全修飾ドメイン名です。つまり、DNSでは、ルートに対して暗黙的に修飾されています([RFC1034]のセクション3.1を参照)。国際化ドメイン名は、[RFC5890]のセクション2.3で説明されているようにA-labelとしてエンコードする必要があります。

<sender>にlocal-partがない場合、local-partを文字列"postmaster"に置き換えます。

4.4 Record Lookup (レコード検索)​

レコードの公開方法(上記のセクション3を参照)に応じて、<domain>名のDNSクエリが必要で、タイプTXTのみです。

DNSクエリがサーバー障害(RCODE 2)または他のエラー(RCODEが0でも3でもない)を返す場合、またはクエリがタイムアウトした場合、check_host()は直ちに"temperror"結果で終了します。

4.5 Selecting Records (レコードの選択)​

レコードはバージョン部分で始まります:

record = version terms *SP
version = "v=spf1"

クエリから返されたレコードセットから始めて、正確に"v=spf1"のバージョン部分で始まらないレコードを破棄します。バージョン部分はSP文字またはレコードの終わりで終了することに注意してください。たとえば、バージョン部分が"v=spf10"のレコードは一致せず、破棄されます。

結果のレコードセットにレコードが含まれていない場合、check_host()は"none"結果を生成します。結果のレコードセットに複数のレコードが含まれている場合、check_host()は"permerror"結果を生成します。

4.6 Record Evaluation (レコードの評価)​

check_host()関数は、SPFレコードを解析および解釈して、現在のテストの結果を見つけます。最初に、レコードの構文が検証され、レコード内のどこかに構文エラーがある場合、check_host()は直ちに"permerror"結果を返し、それ以上の解釈または評価を行いません。

4.6.1 Term Evaluation (用語の評価)​

用語には2つのタイプがあります:メカニズム(セクション5で定義)と修飾子(セクション6で定義)。レコードには、次の拡張バッカス・ナウア記法(ABNF)で指定されているように、これらの順序付きリストが含まれています。

terms = *( 1*SP ( directive / modifier ) )

directive = [ qualifier ] mechanism
qualifier = "+" / "-" / "?" / "~"
mechanism = ( all / include
/ a / mx / ptr / ip4 / ip6 / exists )
modifier = redirect / explanation / unknown-modifier
unknown-modifier = name "=" macro-string
; where name is not any known modifier

name = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )

ほとんどのメカニズムは、名前の後に":"または"/"文字を許可します。

修飾子には、名前の直後、およびmacro-stringの一部である可能性のある":"または"/"文字の前に、常に等号('=')文字が含まれます。

"="、":"、または"/"のいずれの文字も含まない用語は、セクション5で定義されているメカニズムです。

[RFC5234]で定義されているABNF表記法によると、メカニズムと修飾子の名前は大文字と小文字を区別しません。

4.6.2 Mechanisms (メカニズム)​

各メカニズムは、左から右に順番に検討されます。それ以上のメカニズムがない場合、結果はセクション4.7で説明されているデフォルトの結果です。

メカニズムが評価されると、3つのことが起こる可能性があります:一致する、一致しない、または例外を返す。

一致する場合、処理は終了し、修飾子の値がこのレコードの結果として返されます。一致しない場合、処理は次のメカニズムで続行されます。例外を返す場合、メカニズムの処理は終了し、例外値が返されます。

可能な修飾子とそれらがcheck_host()に返させる結果は次のとおりです:

"+" pass
"-" fail
"~" softfail
"?" neutral

修飾子はオプションで、デフォルトは"+"です。

メカニズムが一致し、修飾子が"-"の場合、"fail"結果が返され、セクション6.2で説明されているように説明文字列が計算されます。

セクション5では、特定のメカニズムについて説明します。

4.6.3 Modifiers (修飾子)​

修飾子はメカニズムではありません。一致または不一致を返しません。代わりに、追加情報を提供します。修飾子はレコードの評価に直接影響しませんが、"redirect"修飾子はすべてのメカニズムが評価された後に影響を与えます。

4.6.4 DNS Lookup Limits (DNS検索制限)​

一部のメカニズムと修飾子(総称して"用語"と呼ばれる)は、評価時にDNSクエリを引き起こしますが、他のものはそうではありません。次の用語はDNSクエリを引き起こします:"include"、"a"、"mx"、"ptr"、および"exists"メカニズム、および"redirect"修飾子。SPF実装は、SPF評価中にこれらの用語の合計数を10に制限して、DNSへの不合理な負荷を回避する必要があります。この制限を超えた場合、実装は"permerror"を返す必要があります。他の用語--"all"、"ip4"、および"ip6"メカニズム、および"exp"修飾子--はSPF評価時にDNSクエリを引き起こしません("exp"修飾子は後でのみクエリを引き起こします)、それらの使用はこの制限の対象ではありません。

"mx"メカニズムを評価する場合、照会された"MX"リソースレコードの数は、DNSクエリを引き起こす上記の10メカニズム/修飾子の全体的な制限に含まれます。この制限に加えて、各"MX"レコードの評価は、10を超えるアドレスレコード--"A"または"AAAA"リソースレコード--を照会することは絶対にできません。この制限を超えた場合、"mx"メカニズムは"permerror"結果を生成する必要があります。

"ptr"メカニズムまたは%\{p}マクロを評価する場合、照会された"PTR"リソースレコードの数は、DNSクエリを引き起こす上記の10メカニズム/修飾子の全体的な制限に含まれます。この制限に加えて、各"PTR"レコードの評価は、10を超えるアドレスレコード--"A"または"AAAA"リソースレコード--を照会することは絶対にできません。この制限を超えた場合、最初の10以外のすべてのレコードを無視する必要があります。

違いの理由は、MXレコードのセットと内容は公開ADMDの制御下にあるのに対し、PTRレコードのセットと内容は実際に接続を確立するIPアドレスの所有者の制御下にあるためです。

これらの制限は、レコード内のメカニズムまたはマクロごとであり、上記で指定されたクエリ制限に追加されます。

MTAまたは他のプロセッサは、check_host()の評価に使用される最大経過時間に制限を課す必要があります。そのような制限は少なくとも20秒を許可する必要があります。そのような制限を超えた場合、承認結果は"temperror"である必要があります。

セクション11.1の最後で述べたように、場合によっては、RCODE 0で応答カウント0の肯定応答、または"Name Error"応答(RCODE 3)を返すDNSクエリを返す"用語"の数を制限することが有用である場合があります。これらは、まとめて"void lookups"と呼ばれることがあります。SPF実装は、"void lookups"を2つに制限すべきです(SHOULD)。実装は、そのような制限を設定可能にすることを選択できます(MAY)。その場合、デフォルト値を2にすることをお勧めします。制限を超えると、"permerror"結果が生成されます。

4.7 Default Result (デフォルトの結果)​

メカニズムのいずれも一致せず、"redirect"修飾子がない場合、check_host()は"neutral"結果を返します。これは、"?all"が最後のディレクティブとして指定されているかのようです。"redirect"修飾子が存在する場合、check_host()はセクション6.1で定義されているように続行します。

"redirect"修飾子または"all"メカニズムを使用して、明示的に処理を終了することをお勧めします。明示的に終了しない各レコードの最後に暗黙的な"?all"がありますが、明示的に提供されると、デバッグの取り組みに役立ちます。

例:

v=spf1 +mx -all

または

v=spf1 +mx redirect=_spf.example.com

4.8 Domain Specification (ドメイン仕様)​

これらのメカニズムと修飾子のいくつかには、<domain-spec>部分があります。文字列はマクロ展開の対象となります(セクション7を参照)。結果の文字列は、完全修飾DNS名の通常の表現です:ピリオドで区切られた一連のラベル。このドメインは、このドキュメントの残りの部分で<target-name>と呼ばれます。

注意:マクロ展開の結果は、それ以上のエスケープの対象にはなりません。したがって、このツールは、DNSラベルで合法的なすべての文字(たとえば、制御文字)を生成できません。ただし、このツールは、合法的なホスト名とDNSで使用される一般的なユーティリティラベル(たとえば、"_spf")を表現するのに十分強力です。

いくつかのメカニズムでは、<domain-spec>はオプションです。提供されていない場合、check_host()引数(セクション4.1を参照)からの<domain>が<target-name>として使用されます。"domain"と<target-name>は、マクロ展開後に構文的に同一です。"domain"はcheck_host()の入力値であり、<target-name>はcheck_host()によって計算されます。

構文的に無効なドメインでcheck_host()を評価した結果は未定義です。

注意:このドキュメントとその前身には、[RFC1035]に従って構文的に無効な<domain-spec>(おそらくマクロ展開の結果)を正しく処理するための規定は含まれていません。例には、"foo..example.com"のような空のラベルを持つ名前や、63文字を超える長さのラベルが含まれます。一部の実装は、そのようなエラーを不一致として扱い、そのような名前を無視することを選択しますが、他の実装は"permerror"例外を返します。


5. Mechanism Definitions (メカニズム定義)​

このセクションでは、2つのタイプのメカニズムを定義します:基本言語フレームワークメカニズムと送信者指定メカニズム。

基本メカニズムは言語フレームワークを容易にします。特定のタイプの認証スキームを指定しません。基本メカニズムは次のとおりです:

all
include

送信者指定メカニズムは、<domain>でメールを送信することが許可されている、または許可されていない<ip>アドレスのセットを識別するために使用されます。送信者指定メカニズムは次のとおりです:

a
mx
ptr (do not use)
ip4
ip6
exists

次の規則は、<target-name>とIPアドレスの比較をいつでも実行するすべてのメカニズムに適用されます:

ディレクティブにCIDRプレフィックス長が指定されていない場合、<target-name>はIPアドレスと等しいかどうかで比較されます。(ここで、CIDRは[RFC4632]で説明されているクラスレスドメイン間ルーティングです。)

CIDRプレフィックス長が指定されている場合、<target-name>の指定された数の上位ビットのみがIPアドレスと等しいかどうかで比較されます。

メカニズムが<ip>と比較するホストアドレスを取得する場合、<ip>がIPv4の場合は"A"レコードが取得され、<ip>がIPv6アドレスの場合は"AAAA"レコードが取得されます。IPv6サーバー上のSPF実装は、IPv4マップIPv6アドレス上のクライアントの"AAAA"と"A"レコードの両方を処理する必要があります[RFC4291]。IPv4アドレスは、"ip4"メカニズムを使用してSPFレコードにのみリストされます。

いくつかのメカニズムは、DNSから取得された情報に依存しています。これらのDNSクエリについて、特に指定されていない限り、DNSサーバーがエラーを返す場合(RCODEが0でも3でもない)、またはクエリがタイムアウトする場合、メカニズムは停止し、最上位のcheck_host()は"temperror"を返します。サーバーが"Name Error"(RCODE 3)を返す場合、メカニズムの評価は、サーバーがエラーなし(RCODE 0)とゼロ応答レコードを返したかのように続行されます。

5.1 "all"​

all = "all"

"all"メカニズムは常に一致するテストです。レコード内の最も右のメカニズムとして使用され、明示的なデフォルト値を提供します。

例:

v=spf1 a mx -all

"all"の後のメカニズムはテストされません。"all"の後にリストされているメカニズムは無視する必要があります。レコードに"all"メカニズムが存在する場合、用語の相対的な順序に関係なく、すべての"redirect"修飾子(セクション6.1)を無視する必要があります。

5.2 "include"​

include = "include" ":" domain-spec

"include"メカニズムは、check_host()の再帰的評価をトリガーします。

  1. <domain-spec>はセクション7に従って展開されます。

  2. check_host()は、結果の文字列を<domain>として評価されます。<ip>および<sender>パラメータは、check_host()の現在の評価と同じままです。

  3. 再帰的評価は、一致、不一致、またはエラーを返します。

  4. 一致を返す場合、"include"メカニズムは適切な結果を使用します(たとえば、includeまたは+includeは"pass"結果を生成し、-includeは"fail"を生成します)。

  5. 不一致またはエラーを返す場合、親check_host()は次の表に従って処理を再開し、以前の<domain>値を復元します。

振り返ってみると、"include"という名前は悪い選択でした。参照されたSPFレコードの評価結果のみが使用され、最初のレコードに参照されたレコードのメカニズムを文字通り含めるのではありません。たとえば、参照されたレコードで"-all"ディレクティブを評価しても、全体的な処理は終了せず、必ずしも全体的な"fail"にはなりません。(このメカニズムのより良い名前は"if-match"、"on-match"などだったでしょう。)

"include"メカニズムにより、ドメインは複数の管理上独立したドメインを指定できます。たとえば、バニティドメイン"example.net"は、管理上独立したドメインexample.comとexample.orgのサーバーを使用してメールを送信できます。

Example.netは次のように言えます

IN TXT "v=spf1 include:example.com include:example.org -all"

これにより、check_host()はexample.comとexample.orgのレコードを効果的にチェックして"pass"結果を得るように指示されます。これら2つのドメインのいずれによってもホストが許可されていない場合にのみ、結果は"fail"になります。

このメカニズムが一致するか、一致しないか、または例外を返すかは、check_host()の再帰的評価の結果に依存します:

+---------------------------------+---------------------------------+
| A recursive check_host() result | Causes the "include" mechanism |
| of: | to: |
+---------------------------------+---------------------------------+
| pass | match |
| | |
| fail | not match |
| | |
| softfail | not match |
| | |
| neutral | not match |
| | |
| temperror | return temperror |
| | |
| permerror | return permerror |
| | |
| none | return permerror |
+---------------------------------+---------------------------------+

"include"メカニズムは、管理境界を越えることを目的としています。単一の管理当局内にとどまる場合、"include"は通常最良の選択ではありません。たとえば、example.comとexample.orgが同じエンティティによって管理されており、両方のドメインの許可されたホストのセットが"mx:example.com"である場合、example.orgは"include:example.com"を指定できますが、"redirect=example.com"または"mx:example.com"を指定する方が良いでしょう。

"include"メカニズムを使用すると、管理上外部のホストのセットを承認できますが、送信者ポリシーの決定は元のドメインのSPFレコードの機能のままです(そのレコードの"all"メカニズムによって決定されます)。"redirect"修飾子は、ADMD内で共有される共通のセットに認証とポリシーを統合するのにより適しています。Redirectは、単一のADMD内のレコード間で共有される共通のコード要素に似ています。任意の数のドメインの承認されたホストとポリシーは、単一のレコードから制御できます。

5.3 "a"​

このメカニズムは、<ip>が<target-name>のIPアドレスの1つである場合に一致します。明確にするために、これは"a"メカニズムがAAAAレコードにも一致することを意味します。

a = "a" [ ":" domain-spec ] [ dual-cidr-length ]

<target-name>のアドレスクエリが実行され、接続タイプ(IPv4またはIPv6)に適したクエリタイプ(AまたはAAAA)が使用されます。<ip>は返されたアドレスと比較されます。アドレスが一致する場合、メカニズムは一致します。

5.4 "mx"​

このメカニズムは、<ip>がドメイン名のMXホストの1つである場合に一致します。

mx = "mx" [ ":" domain-spec ] [ dual-cidr-length ]

check_host()は最初に<target-name>のMXクエリを実行します。次に、返された各MX名のアドレスクエリを実行します。<ip>は返された各IPアドレスと比較されます。サービス拒否(DoS)攻撃を防ぐために、セクション4.6.4で定義された処理制限を遵守する必要があります。MXクエリ制限を超えた場合、"permerror"が返され、評価は終了します。アドレスが一致する場合、メカニズムは一致します。

暗黙のMXに関する注意:<target-name>にMXレコードがない場合、check_host()は[RFC5321]の暗黙のMXルールを適用してはなりません。つまり、同じ名前のAまたはAAAAレコードを照会してはなりません。

5.5 "ptr" (do not use) ("ptr" (使用しない))​

このメカニズムは、<ip>のDNS逆マッピングが存在し、特定のドメイン内のドメイン名に正しく指しているかどうかをテストします。このメカニズムは公開すべきではありません。詳細については、このセクションの最後の注記を参照してください。

ptr = "ptr" [ ":" domain-spec ]

<ip>の名前は、次の手順を使用して検索されます:

  • <ip>のDNS逆マッピングを実行します:アドレスがIPv4の場合は"in-addr.arpa."で対応するPTRレコードを検索し、IPv6の場合は"ip6.arpa."で検索します。

  • 返された各レコードについて、そのIPアドレスを検索してドメイン名を検証します。DoS攻撃を防ぐために、セクション4.6.4で定義されたPTR処理制限を適用する必要があります。制限を超えた場合、処理を終了し、メカニズムは一致しません。

  • <ip>が返されたIPアドレスにある場合、このドメイン名は検証されます。

すべての検証されたドメイン名をチェックして、それらが<target-name>と一致するか、<target-name>のサブドメインであるかを確認します。一致がある場合、このメカニズムは一致します。検証されたドメイン名が見つからない場合、または検証されたドメイン名が<target-name>と一致しないか、そのサブドメインでない場合、このメカニズムは一致できません。PTR RRクエリの実行中にDNSエラーが発生した場合、このメカニズムは一致できません。A RRクエリの実行中にDNSエラーが発生した場合、そのドメイン名はスキップされ、検索は続行されます。

このメカニズムは次の場合に一致します:

  • <target-name>が検証されたドメイン名のサブドメインである、または

  • <target-name>と検証されたドメイン名が同一である。

たとえば、"mail.example.com"はドメイン"example.com"内にありますが、"mail.bad-example.com"はそうではありません。

注意:このメカニズムは遅く、DNSエラーの場合に他のメカニズムほど信頼性がなく、.arpaネームサーバーに大きな負荷をかけます。使用する場合、ドメインのホストに適切なPTRレコードを設定する必要があり、"ptr"メカニズムは最後にチェックされるメカニズムの1つである必要があります。数年間のSPF展開経験の後、それは不要であり、より信頼性の高い代替手段を使用すべきであるという結論に達しました。ただし、それはSPFプロトコルの一部として残っているため、適合するcheck_host()実装はそれをサポートする必要があります。

5.6 "ip4" and "ip6" ("ip4"と"ip6")​

これらのメカニズムは、<ip>が指定されたIPネットワークに含まれているかどうかをテストします。

ip4 = "ip4" ":" ip4-network [ ip4-cidr-length ]
ip6 = "ip6" ":" ip6-network [ ip6-cidr-length ]

ip4-cidr-length = "/" ("0" / %x31-39 0*1DIGIT) ; value range 0-32
ip6-cidr-length = "/" ("0" / %x31-39 0*2DIGIT) ; value range 0-128
dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ]

ip4-network = qnum "." qnum "." qnum "." qnum
qnum = DIGIT ; 0-9
/ %x31-39 DIGIT ; 10-99
/ "1" 2DIGIT ; 100-199
/ "2" %x30-34 DIGIT ; 200-249
/ "25" %x30-35 ; 250-255
; as per conventional dotted-quad notation, e.g., 192.0.2.0

ip6-network = <as per [RFC5952], Section 4>
; e.g., 2001:db8::cd30

<ip>を指定されたネットワークと比較します。CIDRプレフィックス長の上位ビットが一致する場合、メカニズムは一致します。

ip4-cidr-lengthが省略されている場合、"/32"として扱われます。ip6-cidr-lengthが省略されている場合、"/128"として扱われます。CIDR表記を使用する代わりにIPアドレスの一部を省略することは許可されていません。つまり、192.0.2の代わりに192.0.2.0/24を使用します。

5.7 "exists"​

このメカニズムは、DNS Aレコードクエリ用の任意のドメイン名を構築するために使用されます。メールエンベロープの任意の部分を含む複雑なスキームを許可して、何が許可されるかを決定します。

exists = "exists" ":" domain-spec

<domain-spec>はセクション7に従って展開されます。結果のドメイン名は、DNS A RRクエリに使用されます(接続タイプがIPv6であっても)。Aレコードが返された場合、このメカニズムは一致します。

ドメインは、このメカニズムを使用して任意に複雑なクエリを指定できます。たとえば、example.comが次のレコードを公開しているとします:

v=spf1 exists:%\{ir}.%\{l1r+-}._spf.%\{d} -all

<target-name>は"1.2.0.192.someuser._spf.example.com"に展開される可能性があります。これにより、ユーザーとクライアントIPアドレスレベルできめ細かい決定を行うことができます。


6. Modifier Definitions (修飾子定義)​

修飾子は、追加情報を提供する名前と値のペアです。修飾子には常に名前と値を区切る"="があります。

このドキュメントで定義されている修飾子("redirect"と"exp")は、レコードの最後、すべてのメカニズムの後に表示されるべきですが、構文的にはレコード内のどこにでも表示できます。これら2つの修飾子の順序は無関係です。これら2つの修飾子は、レコード内に絶対に1回を超えて表示してはなりません。もし表示された場合、check_host()は"permerror"結果で終了します。

認識されない修飾子は、どこに表示されても、何回表示されても、無視する必要があります。これにより、このドキュメントに準拠する実装は、他の仕様で定義された修飾子を持つレコードを適切に処理できます。

6.1 redirect: Redirected Query (redirect: リダイレクトされたクエリ)​

"redirect"修飾子は、単一のADMD内で共有される共通のセットに認証とポリシーを統合することを目的としています。任意の数のドメインの承認されたホストとポリシーは、単一のレコードから制御できます。

redirect = "redirect" "=" domain-spec

すべてのメカニズムが一致せず、"redirect"修飾子が存在する場合、処理は次のように進行します:

redirectの<domain-spec>部分は、セクション7のマクロルールに従って展開されます。次に、check_host()は結果の文字列を<domain>として評価されます。<ip>および<sender>パラメータは、check_host()の現在の評価と同じままです。

この新しいcheck_host()評価の結果は、現在の評価の結果として扱われますが、SPFレコードが見つからない場合、または<target-name>が誤って形式化されている場合、結果は"none"ではなく"permerror"です。

新しいクエリのドメイン自体がredirect処理を指定できることに注意してください。

このツールは、同じレコードを複数のドメインに適用したい組織を対象としています。例:

la.example.com. TXT "v=spf1 redirect=_spf.example.com"
ny.example.com. TXT "v=spf1 redirect=_spf.example.com"
sf.example.com. TXT "v=spf1 redirect=_spf.example.com"
_spf.example.com. TXT "v=spf1 mx:example.com -all"

この例では、これら3つのドメインのいずれかからのメールは、同じレコードによって記述されます。これは管理上の利点となる可能性があります。

注意:一般的に、ドメイン"A"は、同じ管理制御下にない別のドメイン"B"へのリダイレクトを確実に使用できません。<domain>は変更されないため、ドメイン"B"のレコードがドメイン"A"のメールボックスに対して正しく機能する保証はありません。特に、ドメイン"B"がlocal-partを含むメカニズムを使用している場合です。"include"ディレクティブの方が一般的に適切です。

明確にするために、すべての"redirect"修飾子は、レコードの最後の用語として表示されるべきです。レコード内のどこかに"all"メカニズムが存在する場合、すべての"redirect"修飾子を無視する必要があります。

6.2 exp: Explanation (exp: 説明)​

explanation = "exp" "=" domain-spec

check_host()が一致するメカニズム("-all"など)のために"fail"になり、"exp"修飾子が存在する場合、返される説明文字列は以下のように計算されます。"exp"修飾子が存在しない場合、デフォルトの説明文字列または空の説明文字列を呼び出し元アプリケーションに返す必要があります。

<domain-spec>はマクロ展開を受けます(セクション7を参照)、そして<target-name>になります。<target-name>のDNS TXT RRsetが取得されます。

DNSの処理エラーがある場合(RCODE 0以外)、レコードが返されない場合、複数のレコードが返される場合、または説明文字列に構文エラーがある場合、"exp"修飾子が与えられていないかのように続行します。

取得されたTXTレコードの文字列は、スペースなしで連結され、その後explain-stringとして処理され、マクロ展開を受けます。この最終結果が説明文字列です。実装は、他のプロトコル制約および/または合理的な処理制限を考慮して、結果の説明文字列の長さを制限できます(MAY)。説明文字列はSMTP応答で使用することを目的としており、[RFC5321]のセクション2.4は応答が[US-ASCII]であると述べているため、説明文字列は[US-ASCII]に制限する必要があります。

check_host()を評価するソフトウェアは、この文字列を使用して、短いメッセージまたはURLの形式で公開ドメインから情報を伝達できます。ソフトウェアは、説明文字列が第三者から来ていることを明確にする必要があります。たとえば、セクション8.4の例に示されているように、マクロ文字列"%\{o} explains: "を説明の前に付けることができます。

example.comに次のレコードがあるとします:

v=spf1 mx -all exp=explain._spf.%\{d}

以下は、explain._spf.example.comでの可能な説明TXTレコードの例です:

"Mail from example.com should only be sent by its own servers."

-- シンプルで一定のメッセージ

"%\{i} is not one of %\{d}'s designated mail servers."

-- チェックに失敗したIPアドレスを含む、より多くの情報を含むメッセージ

"See http://%\{d}/why.html?s=%\{S}&i=%\{I}"

-- check_host()のパラメータを使用してURLを構築する複雑な例で、詳細でカスタマイズされた手順を含むWebページを生成できるようにします

注意:"include"メカニズムへの再帰中、<target-name>からの"exp"修飾子は絶対に使用してはなりません。逆に、"redirect"修飾子を実行する場合、元のドメインからの"exp"修飾子は絶対に使用してはなりません。これは、"include"が管理境界を越えることを目的としており、提供される説明は受信ADMDからのものであるべきであるのに対し、"redirect"はADMD内でポリシーレコードを統合するツールとして意図されており、したがってリダイレクトされた説明が優先されるべきものだからです。


7. Macros (マクロ)​

SPFポリシーレコードを評価する際、特定の文字シーケンスは、メッセージまたは接続のパラメータに置き換えられることを意図しています。これらの文字シーケンスは"マクロ"と呼ばれます。

7.1 Formal Specification (正式仕様)​

マクロのABNF記述は次のとおりです:

domain-spec = macro-string domain-end
domain-end = ( "." toplabel [ "." ] ) / macro-expand

explain-string = *( macro-string / SP )

macro-string = *( macro-expand / macro-literal )
macro-literal = %x21-24 / %x26-7E
; visible characters except "%"

macro-expand = ( "%\{" macro-letter transformers *delimiter "}" )
/ "%%" / "%_" / "%-"

macro-letter = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
"c" / "r" / "t" / "v"

transformers = *DIGIT [ "r" ]
delimiter = "." / "-" / "+" / "," / "/" / "_" / "="

toplabel = ( *alphanum ALPHA *alphanum ) /
( 1*alphanum "-" *( alphanum / "-" ) alphanum )
; LDH rule plus additional TLD restrictions
; (see [RFC5890], Section 2.3.1)

alphanum = ALPHA / DIGIT

ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
DIGIT = %x30-39 ; 0-9

7.2 Macro Definitions (マクロ定義)​

利用可能なマクロ文字とその意味は次のとおりです:

  • s = <sender>
  • l = <sender>のlocal-part
  • o = <sender>のdomain
  • d = <domain>
  • i = <ip>
  • p = <ip>の検証されたドメイン名
  • v = PTRクエリのin-addr.arpa文字列、ip4の場合は"in-addr"、ip6の場合は"ip6"
  • h = HELO/EHLOドメイン

7.3 Macro Processing Details (マクロ処理の詳細)​

このセクションでは、マクロ展開のプロセスを詳細に説明します。

展開されるマクロ文字列は、"%"文字で始まるか、他のすべての文字で構成されるフラグメントに分解されます。"%"で始まるフラグメントは"マクロ展開"と呼ばれます。他のすべてのフラグメントは"マクロリテラル"と呼ばれます。

各マクロ展開は置き換えられて(展開されて)、結果文字列を生成します。この結果文字列内のDNS名ラベルは"."文字で区切られ、各ラベルはLDH(Letter-Digit-Hyphen)ルールで構成される必要があります:完全にASCII小文字、ASCII数字、および/またはハイフンで構成される必要があります。マクロリテラルは、結果内で変更されません。マクロ展開とマクロリテラルの間に文字は挿入されません。

展開以外に、結果文字列に対してエスケープやその他の後処理は実行されません。そのまま使用されます。これは、ASCII NUL文字が不要であり、結果文字列でそのような文字を生成するメカニズムが提供されていないことを意味します。

マクロ文字は、セクション7.2で説明されているように、定義された値に展開され、次の変換が適用されます。変換構文はtransformersコンポーネントによって定義されます。

r transformer:

文字"r"は、展開前に値を逆にする必要があることを示します。IPv6アドレスとドメイン名の場合、逆転はドット区切り文字で実行されます。IPv4アドレスの場合、逆転はドット境界で実行されます。

DIGIT transformer:

1つ以上の数字は、値の終わりから抽出するドットで区切られた部分の数を示します。数字が存在する場合、右(最上位値)から使用するラベルの数を示します。

数字が指定されていない場合、すべてのラベルが使用されます。数字ゼロは、空の文字列を使用することを示します。文字列は、トランスフォーマーの適用後に切り捨てられます。

Delimiters:

1つ以上の区切り文字は、他のトランスフォーマーを適用した後に区切り文字"."に置き換える文字を示します。区切り文字は値の自然な部分ではないことに注意してください。マクロ展開中に適用されます。以下は区切り文字とその展開です:

  • %\{s} = <sender>
  • %\{o} = <sender>のドメイン部分
  • %\{d} = <domain>
  • %\{d4} = <domain>の最後の4つのラベル
  • %\{d4r} = <domain>の最後の4つのラベル、逆順
  • %\{l} = <sender>のlocal-part
  • %\{l-} = local-part、"."が"-"に置き換えられる
  • %\{lr} = local-part、逆順
  • %\{lr-} = local-part、逆順、"."が"-"に置き換えられる
  • %\{l1r-} = local-partの最初の部分、逆順、"."が"-"に置き換えられる

7.4 Expansion Examples (展開例)​

以下は、マクロ展開の例です。次のように仮定します:

<sender> = [email protected]
<domain> = email.example.com
<ip> = 192.0.2.3

マクロ展開例:

マクロ展開結果
%\{s}[email protected]
%\{o}email.example.com
%\{d}email.example.com
%\{d4}email.example.com
%\{d3}email.example.com
%\{d2}example.com
%\{d1}com
%\{dr}com.example.email
%\{d2r}example.email
%\{l}strong-bad
%\{l-}strong-bad
%\{lr}strong-bad
%\{lr-}strong-bad
%\{l1r-}strong-bad
%\{ir}.%\{v}._spf.%\{d2}3.2.0.192.in-addr._spf.example.com
%\{lr-}.lp._spf.%\{d2}strong-bad.lp._spf.example.com
%\{lr-}.lp.%\{ir}.%\{v}._spf.%\{d2}strong-bad.lp.3.2.0.192.in-addr._spf.example.com
%\{ir}.%\{v}.%\{l1r-}.lp._spf.%\{d2}3.2.0.192.in-addr.strong-bad.lp._spf.example.com
%\{d2}.trusted-domains.example.netexample.com.trusted-domains.example.net

8. Result Handling (結果処理)​

このセクションでは、SPF実装がcheck_host()のさまざまな可能な結果をどのように処理すべきかについて説明します。これはアドバイザリーにすぎません。ローカルサイトポリシーが特定の結果に対して異なる措置を決定する可能性があるためです。

8.1 None​

「none」の返却は、照会されたドメインに一致する適用可能な送信者ポリシーが見つからなかったことを意味します。この結果は「SPFが存在しない」と同等として扱われるべきです。つまり、評価すべきポリシーがありません。

受信MTAは、この結果に基づいてメールを拒否すべきではありません。SPF検証の失敗は拒否につながる可能性がありますが、「none」結果は送信者の認可に関する情報を提供しないため、拒否の基礎として使用すべきではありません。

8.2 Neutral​

「neutral」の返却は、ドメインの所有者が送信ホストが認可されているかどうかを主張しないことを明示的に宣言したことを意味します。この結果は「none」結果と同等として扱われるべきです。SPF検証はホストの認可を確認も否定もできません。

受信MTAは、この結果に基づいてメールを拒否すべきではありません。

8.3 Pass​

「pass」の返却は、クライアントが送信者ポリシーテストに合格したことを意味します。受信MTAは他のスパム対策テストを続行できます。

受信MTAは、この結果に基づいてメールを拒否すべきでは絶対にありません。

8.4 Fail​

「fail」の返却は、クライアントが送信者ポリシーテストに合格しなかったことを意味します。受信MTAはメールを拒否できます。

公開ADMDが説明文字列を返すことを選択した場合(「exp」修飾子を使用)、その文字列はSMTP応答の一部として返されるべきです。説明文字列が提供されていない場合、受信MTAは一般的な説明を使用すべきです。

送信者に返されるSMTP応答に、公開ドメインに関する情報およびそのドメインの連絡先情報(利用可能な場合)を含めることが推奨されます。これはデバッグに役立ち、正当なメールがブロックされるのを防ぐのに役立つ可能性があります。

SMTP応答の例:

550-5.7.1 SPF MAIL FROM check failed:
550-5.7.1 The domain example.com explains:
550 5.7.1 Please see http://www.example.com/mailpolicy.html

8.5 Softfail​

「softfail」の返却は、ホストがメールを送信することを認可されていない可能性があるが、移行がまだ完了していないことを意味します。

受信MTAは、この結果に基づいてメールを拒否すべきではありませんが、何らかの方法で疑わしいとマークすることができます。たとえば、受信MTAはメールをスパムフォルダに入れたり、ヘッダーを追加したりできます。

8.6 Temperror​

「temperror」の返却は、SPF評価中に一時的なエラーが発生したことを意味します。受信MTAはメールを受け入れるべきですが、後でSPF検証を再試行したい場合があります。

たとえば、DNSサーバーが一時的に利用できない場合、受信者はメールを受け入れ、後でSPFポリシーを再評価したい場合があります。

8.7 Permerror​

「permerror」の返却は、SPF評価中に永続的なエラーが発生したことを意味します。これは、ドメインのSPFレコードの構文エラーまたは他の構成問題が原因である可能性があります。

受信MTAはメールを拒否できますが、問題の診断に役立つようにエラーに関する情報もログに記録すべきです。


9. Recording the Result (結果の記録)​

SPFチェックが実行された後、受信サーバーは、後続の処理と分析のために結果を記録する必要があります。


9.1 The Received-SPF Header Field (Received-SPFヘッダーフィールド)​

受信サーバーは、SPF検証の結果をReceived-SPFヘッダーフィールドに記録すべきです(SHOULD)。このフィールドは電子メールメッセージに追加され、SPF評価の永続的な記録を提供します。

Syntax (構文)​

Received-SPF: result
[comment]
receiver=receiving-domain;
identity=identity;
envelope-from=<sender>;
helo=helo-identity;
client-ip=client-ip-address;
[receiver-policy-explanation]

Field Components (フィールドコンポーネント)​

  • result: SPFチェックの結果(None、Neutral、Pass、Fail、SoftFail、TempError、PermError)
  • receiver: 受信メールサーバーのホスト名
  • identity: どのIDが検証されたか(mailfrom、helo)
  • envelope-from: MAIL FROMアドレス
  • helo: HELO/EHLOのID
  • client-ip: クライアントのIPアドレス

Example (例)​

Received-SPF: pass (example.com: domain of [email protected]
designates 192.0.2.1 as permitted sender)
receiver=mail.example.com;
identity=mailfrom;
[email protected];
helo=mail.example.com;
client-ip=192.0.2.1

9.2 SPF Results in Authentication-Results (Authentication-Results内のSPF結果)​

受信者は、Authentication-Resultsヘッダーフィールド(RFC 7001で定義)を使用して、DKIMやDMARCなどの他の電子メール認証方法と並んでSPF結果を記録することもできます(MAY)。

Example (例)​

Authentication-Results: mail.example.com;
spf=pass [email protected]

9.3 Local Policy Storage (ローカルポリシーストレージ)​

受信サーバーは、ログ、データベース、またはレピュテーションシステムでの内部使用のためにSPF結果を保存できます(MAY)。これらの記録は以下に使用できます:

  • スパムフィルタリング: スパム評価のシグナルとしてSPF結果を使用
  • レポート: SPF統計とトレンドの生成
  • レピュテーション: SPFコンプライアンスに基づく送信者レピュテーションの構築
  • フォレンジック: 電子メール悪用とフィッシング試行の分析

9.4 Downstream Processing (下流処理)​

SPF結果は、電子メール送信パス全体で保持されるべきです。受信サーバーがメッセージを別のサーバーに転送する場合、元のReceived-SPFヘッダーフィールドを保持して、下流システムが元のSPF検証結果を確認できるようにする必要があります。


10. Effects on Infrastructure (インフラストラクチャへの影響)​

SPFの実装は、電子メールおよびDNSインフラストラクチャにさまざまな影響を与える可能性があります。管理者は、これらの潜在的な影響を認識する必要があります。


10.1 DNS Server Load (DNSサーバーの負荷)​

SPFチェックには追加のDNSクエリが必要であり、DNSサーバーの負荷が増加する可能性があります。各受信電子メールに対して、受信者は次のことを行う必要があります:

  • 送信ドメインのSPFレコードを照会
  • メカニズムの追加のDNSルックアップを実行(A、MX、PTR、EXISTS、INCLUDE)
  • include:メカニズムの再帰的なクエリを実行

Mitigation (緩和)​

  • DNSキャッシング: TTL値の適切な使用により、冗長なクエリを最小限に抑える
  • ルックアップ制限: SPFはDNSルックアップの数を10に制限し、過度の負荷を防止
  • 効率的なSPFレコード設計: メカニズムとincludeの数を最小化

10.2 Large Volume Mailing (大量メール送信)​

大量の電子メールを送信する組織は、SPFレコードが次のことを確認する必要があります:

  • すべての承認された送信IPアドレスをカバー: すべての正規のメールサーバー、サードパーティ、クラウドサービスを含める
  • 正しく構造化: PermErrorまたは過剰なDNSルックアップを回避
  • 定期的に更新: 送信インフラストラクチャが変更されたとき

Third-Party Senders (サードパーティ送信者)​

サードパーティの電子メールサービス(マーケティングプラットフォーム、クラウドサービスなど)を使用する組織は、これらのサービスをSPFレコードで承認する必要があります:

v=spf1 include:_spf.example.com include:_spf.thirdparty.com -all

10.3 Email Forwarding (電子メール転送)​

SPFは電子メール転送シナリオで問題を引き起こす可能性があります:

問題: 受信者が電子メールを別の受信者に転送すると、送信IPアドレスが変わります(現在は転送サーバー)が、MAIL FROMアドレスは元のままです。これによりSPFが失敗します。

Solutions (ソリューション)​

  • SRS (Sender Rewriting Scheme): 転送中にMAIL FROMアドレスを書き換え
  • SPF NeutralまたはSoftFail: 影響を減らすために-allの代わりに~allを使用
  • DMARC統合: 転送シナリオを処理するためにDKIMと共にDMARCを使用

10.4 Mailing Lists (メーリングリスト)​

メーリングリストは、電子メール転送と同様の課題に直面しています:

  • メーリングリストサーバーは、元の送信者から多くの購読者にメッセージを転送
  • MAIL FROMアドレスは元の送信者のままのことが多いが、送信IPはメーリングリストサーバー

Best Practices (ベストプラクティス)​

  • MAIL FROM書き換え: メーリングリストソフトウェアは、MAIL FROMアドレスをメーリングリストドメインに書き換える必要がある
  • DKIM署名: メーリングリストは独自のDKIM署名を追加する必要がある
  • List-IDヘッダー: List-IDおよびその他のヘッダーを使用してメーリングリストの起源を識別

10.5 IPv6 Considerations (IPv6に関する考慮事項)​

SPFはIPv4とIPv6の両方をサポートしています。管理者は次のことを行う必要があります:

  • ip6メカニズムを含める: 送信インフラストラクチャがIPv6をサポートしている場合
v=spf1 ip4:192.0.2.0/24 ip6:2001:db8::/32 -all
  • デュアルエントリを維持: IPv4とIPv6の両方の範囲がカバーされていることを確認
  • DNS AAAAレコード: AおよびMXメカニズムがIPv6アドレスを適切に解決することを確認

10.6 Subdomain Policies (サブドメインポリシー)​

デフォルトでは、サブドメインは親ドメインからSPFレコードを継承しません。電子メールを送信する各サブドメインには独自のSPFレコードが必要です。または、親ドメインはワイルドカードレコードを公開する必要があります:

*.example.com. IN TXT "v=spf1 -all"

これにより、攻撃者が存在しないサブドメインをスプーフィングに使用することを防ぎます。


11. Security Considerations (セキュリティに関する考慮事項)​

SPFは電子メールに重要なセキュリティの改善を提供しますが、特定の制限と潜在的なセキュリティリスクもあります。


11.1 Security Strengths (セキュリティの強み)​

SPFは次のセキュリティ上の利点を提供します:

Email Source Authentication (電子メールソース認証)​

SPFは、電子メールが特定のドメインの承認されたメールサーバーから送信されたことを受信者が検証できるようにします。これにより、次のことが削減されます:

  • ドメインスプーフィング: 攻撃者が正規のドメインから送信しているふりをすることを防ぐ
  • フィッシング: 攻撃者が信頼できるソースから来たように見える電子メールを作成することをより困難にする
  • スパム: 未承認の送信者からの不要な電子メールを識別するのに役立つ

Lightweight and Scalable (軽量でスケーラブル)​

SPFは既存のDNSインフラストラクチャを使用するため、新しいプロトコルやサーバーを必要とせず、展開と保守が容易です。


11.2 Security Limitations (セキュリティの制限)​

SPFには、管理者が理解する必要がある重要な制限があります:

Does Not Authenticate Message Content (メッセージコンテンツを認証しない)​

SPFはMAIL FROMアイデンティティ(エンベロープFrom)のみを検証し、エンドユーザーが見るFromヘッダーは検証しません。攻撃者は依然として次のことができます:

  • Fromヘッダーを偽造し、表示されるFromフィールドで異なるドメインを使用
  • 有効なSPFレコードを持つ独自のドメインを使用してSPFをバイパス

ソリューション: SPFと共にDMARCを使用して、Fromヘッダーの整合性を強制します。

Does Not Protect Message Integrity (メッセージの整合性を保護しない)​

SPFは、電子メールコンテンツの暗号署名または整合性チェックを提供しません。攻撃者は転送中にメッセージコンテンツを変更できます。

ソリューション: メッセージの暗号署名と整合性保護のためにDKIMを使用します。

Forwarding Issues (転送の問題)​

セクション10.3で説明したように、SPFは電子メール転送とメーリングリストのシナリオで失敗します。送信IPは変わりますが、MAIL FROMアイデンティティは同じままだからです。

ソリューション: SRS (Sender Rewriting Scheme)またはDMARCポリシー処理を使用します。


11.3 DNS Security (DNSセキュリティ)​

SPFはDNSに依存しており、DNSはさまざまな攻撃に対して脆弱である可能性があります:

DNS Spoofing and Cache Poisoning (DNSスプーフィングとキャッシュポイズニング)​

攻撃者は、SPFチェックをバイパスまたは欺くためにDNS応答を操作しようとする可能性があります。

緩和: DNS応答の暗号認証のためにDNSSECを使用します。

DNS Lookup Amplification (DNSルックアップの増幅)​

SPFレコードは、過剰なDNSルックアップを引き起こすように設計され、サービス拒否(DoS)攻撃につながる可能性があります。

緩和: SPFは検証ごとに10 DNSルックアップの制限を課します。実装はこの制限を強制し、超過した場合はPermErrorで中止する必要があります。


11.4 Privacy Considerations (プライバシーに関する考慮事項)​

SPFチェックには、送信者のIPアドレスを明らかにするDNSクエリが必要です:

  • ドメイン管理者はDNSクエリをログに記録し、送信IPに関する情報を収集できます
  • ISPとDNSリゾルバはSPFクエリトラフィックを監視できます

これらは一般的に深刻なプライバシーの問題ではありませんが、管理者はそれを認識しておく必要があります。


11.5 DoS Attacks (DoS攻撃)​

SPFチェックにはDNSルックアップが必要であり、DoS攻撃に悪用される可能性があります:

Excessive DNS Queries (過剰なDNSクエリ)​

攻撃者は、多くのDNSルックアップをトリガーする複雑なSPFレコードを含む電子メールを送信し、受信者のDNSサーバーを過負荷にする可能性があります。

緩和: 10ルックアップ制限を強制し、PermErrorで処理を中止します。

DNS Reflection Attacks (DNSリフレクション攻撃)​

攻撃者はSPF処理を悪用してDNSリフレクション攻撃を増幅する可能性があります。

緩和: DNSクエリのレート制限と監視を実装します。


11.6 Best Practices for Secure SPF Deployment (安全なSPF展開のベストプラクティス)​

SPF展開のセキュリティを最大化するには:

  1. Use SPF with DKIM and DMARC (SPFをDKIMおよびDMARCと共に使用): メッセージ署名のためにSPFをDKIMと組み合わせ、ポリシー適用のためにDMARCを使用します。

  2. Deploy DNSSEC (DNSSECを展開): DNSスプーフィングを防ぐためにDNSSECでDNS応答を保護します。

  3. Use Hard Fail Cautiously (Hard Failを慎重に使用): -all(Fail)に移行する前に、テストのために~all(SoftFail)から始めます。

  4. Monitor and Maintain SPF Records (SPFレコードを監視および保守): メールインフラストラクチャが変更されたときにSPFレコードを定期的に更新します。

  5. Limit DNS Lookups (DNSルックアップを制限): SPFレコードをシンプルに保ち、過剰なinclude:およびredirect:メカニズムを避けます。

  6. Implement Logging and Monitoring (ログ記録と監視を実装): SPF検証結果を追跡し、異常を調査します。

  7. Educate Users (ユーザーを教育): ユーザーにフィッシングとFromヘッダー検証におけるSPFの制限について知らせます。


Appendix A. Extended Examples (拡張例)​

この付録は、さまざまな設定シナリオとベストプラクティスを示すSPFレコードの拡張例を提供します。

A.1 Simple Examples (簡単な例)​

A.1.1 単一のIPアドレスのみを許可​

example.com. IN TXT "v=spf1 ip4:192.0.2.1 -all"

説明: IPアドレス192.0.2.1のみがexample.comを代表してメールを送信することが許可されます。他のすべてのIPアドレスは"fail"結果になります。

A.1.2 MXレコードの使用​

example.com. IN TXT "v=spf1 mx -all"

説明: example.comのMXレコードにリストされているメールサーバーがメールを送信することを許可します。これはMXレコードの変更に自動的に適応するため、一般的な設定です。

A.1.3 Aレコードの使用​

example.com. IN TXT "v=spf1 a -all"

説明: example.comのAレコード(またはIPv6の場合はAAAAレコード)内のIPアドレスがメールを送信することを許可します。

A.1.4 複数のメカニズムの組み合わせ​

example.com. IN TXT "v=spf1 mx a:mail.example.com ip4:192.0.2.0/24 -all"

説明: 次のソースからのメールを許可します:

  • example.comのMXサーバー
  • mail.example.comのAレコード
  • 192.0.2.0/24ネットワーク内の任意のIP

A.1.5 ソフトフェイルポリシー​

example.com. IN TXT "v=spf1 mx a ~all"

説明: ハードフェイル(-all)の代わりにソフトフェイル(~all)を使用します。これはテストまたは移行フェーズで役立ちます。受信者はメールを受け入れますが、疑わしいものとしてマークする可能性があります。

A.2 Multiple Domain Example (複数ドメイン例)​

同じメールインフラストラクチャを使用する複数のドメインを持つ組織の場合:

example.com.     IN TXT "v=spf1 mx -all"
example.org. IN TXT "v=spf1 redirect=_spf.example.com"
example.net. IN TXT "v=spf1 redirect=_spf.example.com"
_spf.example.com. IN TXT "v=spf1 mx:example.com -all"

説明:

  • example.comには独自のSPFレコードがあります
  • example.orgとexample.netは共有SPFレコードにリダイレクトします
  • _spf.example.comには実際のポリシーが含まれています

利点:

  • メールサーバー設定の集中管理
  • 1回の更新で複数のドメインに影響します
  • DNSメンテナンスの削減

A.3 DNS Blacklist (DNSBL) Style Example (DNSブラックリストスタイル例)​

existsメカニズムを使用してDNSBL類似の機能を実装:

example.com. IN TXT "v=spf1 exists:%\{ir}.%\{l1r+-}._spf.%\{d} -all"

説明:

  • %\{ir}: 反転したIPアドレス(例: 192.0.2.1が1.2.0.192になる)
  • %\{l1r+-}: 送信者のlocal-partの最初の部分、反転、"."と"+"が"-"に置き換えられる
  • %\{d}: ドメイン名

展開例:

送信者が192.0.2.1から[email protected]を送信する場合:

  • マクロは次のように展開されます: 1.2.0.192.user._spf.example.com

次に、このドメイン名のAレコードがクエリされます。存在する場合、SPFチェックは成功します。

使用例:

  • ユーザーベースのきめ細かい制御
  • データベースまたはカスタムシステムとの統合
  • 動的な認証決定

A.4 Multiple Requirements Example (複数要件例)​

複数のメカニズムと修飾子を組み合わせた複雑な設定:

example.com. IN TXT "v=spf1 ip4:192.0.2.0/24 ip4:198.51.100.0/24 include:_spf-servers.example.com include:_spf.google.com a:outbound.example.com mx ~all"

説明:

  • ip4:192.0.2.0/24: 内部メールサーバーネットワーク
  • ip4:198.51.100.0/24: バックアップデータセンターネットワーク
  • include:_spf-servers.example.com: 追加サーバーリストを含める
  • include:_spf.google.com: Google Workspaceを使用
  • a:outbound.example.com: 特定のアウトバウンドサーバー
  • mx: MXレコードサーバーを含める
  • ~all: 他のすべての場合にソフトフェイル

階層設計:

_spf-servers.example.com. IN TXT "v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.128/25 -all"

これにより、大きなSPFレコードをより管理しやすい部分に分割できます。

A.5 サブドメイン設定例​

A.5.1 異なるポリシーを持つサブドメイン​

example.com.       IN TXT "v=spf1 mx -all"
mail.example.com. IN TXT "v=spf1 a -all"
shop.example.com. IN TXT "v=spf1 include:shopify.com -all"

説明:

  • メインドメインはMXレコードを使用
  • mailサブドメインはAレコードのみを使用
  • shopサブドメインはサードパーティサービス(Shopify)を使用

A.5.2 メールを送信しないサブドメイン​

noreply.example.com. IN TXT "v=spf1 -all"
static.example.com. IN TXT "v=spf1 -all"

説明: これらのサブドメインがメールを送信しないことを明示的に宣言し、偽造を防ぎます。

A.6 サードパーティサービス統合例​

A.6.1 複数のメールサービスプロバイダーの使用​

example.com. IN TXT "v=spf1 include:_spf.google.com include:spf.protection.outlook.com include:sendgrid.net -all"

説明:

  • 従業員メール用のGoogle Workspace
  • パートナー用のMicrosoft 365
  • マーケティングメール用のSendGrid

A.6.2 DNSクエリ制限の確認​

上記の例は3つのincludeを使用しており、それぞれが追加のクエリをトリガーする可能性があります。クエリの合計数が10を超えないことを確認する必要があります。

検証方法:

# GoogleのSPFを確認
dig _spf.google.com TXT

# MicrosoftのSPFを確認
dig spf.protection.outlook.com TXT

# SendGridのSPFを確認
dig sendgrid.net TXT

各includeのメカニズム数を計算し、合計が≤10であることを確認します。

A.7 誤設定例(回避すべき)​

A.7.1 ❌ 複数のSPFレコード(エラー)​

example.com. IN TXT "v=spf1 mx -all"
example.com. IN TXT "v=spf1 a -all"

問題: これは"permerror"になります。ドメインには1つのSPFレコードしか持てません。

正しい方法:

example.com. IN TXT "v=spf1 mx a -all"

A.7.2 ❌ -allの欠落(安全でない)​

example.com. IN TXT "v=spf1 mx"

問題: 明示的なデフォルトポリシーがなく、?all(neutral)と同等で、どのIPも失敗しません。

正しい方法:

example.com. IN TXT "v=spf1 mx -all"

A.7.3 ❌ DNSクエリ制限の超過​

example.com. IN TXT "v=spf1 include:a include:b include:c include:d include:e include:f include:g include:h include:i include:j include:k -all"

問題: 11個のincludeは10クエリ制限を超え、"permerror"になります。

解決策: SPFフラット化を使用するか、ip4メカニズムを直接使用します。

A.8 IPv6設定例​

example.com. IN TXT "v=spf1 ip6:2001:db8::/32 ip4:192.0.2.0/24 mx -all"

説明: IPv6とIPv4ネットワークの両方をサポートします。

A.9 説明文字列の例​

example.com.         IN TXT "v=spf1 mx -all exp=explain._spf.%\{d}"
explain._spf.example.com. IN TXT "%\{i}からのメールですが、このIPは%\{d}によって承認されていません。postmaster@%\{d}に連絡してください。"

説明: SPFチェックが失敗した場合、受信者は説明文字列をクエリしてユーザーに表示できます。

マクロ展開後(192.0.2.99から送信と仮定):

"192.0.2.99からのメールですが、このIPはexample.comによって承認されていません。[email protected]に連絡してください。"

A.10 テストと移行戦略​

フェーズ1: 監視モード(2-4週間)​

example.com. IN TXT "v=spf1 ?all"

フェーズ2: 実際の送信元を記録(2-4週間)​

example.com. IN TXT "v=spf1 ip4:192.0.2.0/24 include:provider.com ?all"

フェーズ3: ソフトフェイル(4-8週間)​

example.com. IN TXT "v=spf1 ip4:192.0.2.0/24 include:provider.com ~all"

フェーズ4: 厳格モード(本番環境)​

example.com. IN TXT "v=spf1 ip4:192.0.2.0/24 include:provider.com -all"

推奨: 各フェーズでSPFチェック結果を監視し、正当なメールがブロックされていないことを確認します。


Appendix B. Changes in Implementation Requirements from RFC 4408 (RFC 4408からの実装要件の変更)​

この付録は、RFC 7208がその前身であるRFC 4408に対して行った主要な変更をまとめています。

B.1 主要な変更​

B.1.1 SPF RRタイプの廃止​

RFC 4408の要件:

  • TXTレコードとSPF(タイプ99)レコードの同時公開
  • 実装は両方のレコードタイプをチェックする必要がある

RFC 7208の変更:

  • TXTレコードのみを使用(タイプ16)
  • SPF RRタイプはサポートされなくなった
  • 実装と展開の簡素化

理由: SPF RRタイプの採用率が非常に低く、デュアルタイプシステムが相互運用性の問題を引き起こしていた。

B.1.2 DNSクエリ制限の明確化​

RFC 4408:

  • DNSクエリ制限の記述が不十分
  • "void lookup"制限が未定義

RFC 7208:

  • 10 DNSクエリ制限の明確な定義
  • "void lookup"制限の追加(推奨2)
  • MXおよびPTRメカニズムの追加制限の詳細な説明

具体的な制限:

- 総DNSクエリ: ≤ 10 (include, a, mx, ptr, exists, redirect)
- mxメカニズムごと: ≤ 10アドレスクエリ
- ptrメカニズムごと: ≤ 10アドレスクエリ
- Void lookups: ≤ 2 (推奨)
- 評価時間: ≥ 20秒 (推奨)

B.1.3 "local-part"の処理​

RFC 4408:

  • local-part処理が不十分
  • 空のlocal-partの処理が未定義

RFC 7208:

  • 明確な規定: <sender>にlocal-partがない場合、"postmaster"を使用
  • 空のリバースパス(<>)の処理を改善

例:

MAIL FROM:<>
→ SPFはHELO識別を使用、local-partは"postmaster"

B.1.4 "ptr"メカニズムへの強い反対​

RFC 4408:

  • "ptr"メカニズムは許可されているが推奨されていない

RFC 7208:

  • "do not use"(使用しない)として明確にマーク
  • メカニズム名に警告を追加
  • パフォーマンスと信頼性の問題を強調

理由:

  • DNS逆引きクエリが遅い
  • サードパーティ(IPアドレス所有者)の設定に依存
  • .arpaネームサーバーに負荷をかける

B.1.5 マクロ構文の修正​

RFC 4408:

  • マクロ構文定義が曖昧

RFC 7208:

  • ABNF定義の改善
  • マクロ展開ルールの明確化
  • 境界ケースの修正

B.1.6 "exp"修飾子のセキュリティ考慮事項​

RFC 4408:

  • セキュリティ考慮事項が不十分に詳細

RFC 7208:

  • 外部説明文字列に関するセキュリティ警告を追加
  • 説明文字列の長さを制限することを推奨
  • 説明がサードパーティからのものであることを識別する必要性を強調

B.2 実装要件の変更​

B.2.1 MUST変更(実装する必要がある)​

新しい要件:

  1. 実装はTXTレコードのみをクエリする必要がある(SPF RRはもうない)
  2. 実装は総DNSクエリ数を10に制限する必要がある
  3. 実装は"void lookup"制限を処理する必要がある
  4. 実装は制限を超えた場合に"permerror"を返す必要がある

削除された要件:

  1. SPF RRタイプのサポートは不要
  2. デュアルタイプレコードの変換ロジックは不要

B.2.2 SHOULD変更(実装すべき)​

新しい推奨事項:

  1. 評価タイムアウトを実装すべき(少なくとも20秒)
  2. "void lookup"を2に制限すべき
  3. HELOとMAIL FROM識別の両方をチェックすべき
  4. SMTPトランザクション中にチェックを実行すべき

B.2.3 MAY変更(実装してもよい)​

新しいオプション:

  1. 非標準アルゴリズムを使用してもよい(結果が同じである限り)
  2. "void lookup"制限を設定してもよい
  3. 説明文字列の長さを制限してもよい

B.3 用語の変更​

B.3.1 識別名の標準化​

RFC 4408:

  • 複数の名前を使用: "MAIL FROM", "SMTP MAIL FROM", "reverse-path"
  • 一貫性のない用語

RFC 7208:

  • "MAIL FROM"識別の統一使用
  • RFC5321.MailFromとして明確に定義
  • RFC 5598標準用語への参照

B.3.2 "ADMD"用語の採用​

RFC 4408:

  • "domain owner", "sending domain"を使用

RFC 7208:

  • "ADMD"(Administrative Management Domain)の採用
  • 責任者のより正確な記述

B.4 セキュリティ考慮事項の強化​

B.4.1 新しいセキュリティトピック​

  1. クロスユーザー偽装(セクション11.4):

    • RFC 4408ではカバーされていない
    • RFC 7208はドメイン内ユーザー偽装問題を明示的に議論
  2. プライバシー露出(セクション11.6):

    • DNSクエリ内のマクロが機密情報を漏らす可能性
    • 送信者情報を含むマクロを慎重に使用することを推奨
  3. 信頼できない情報源(セクション11.5):

    • 外部ヘッダーと説明のリスクの詳細な議論
    • 検証とフィルタリングの重要性を強調

B.4.2 DoS保護の強化​

RFC 4408:

  • 基本的なクエリ制限

RFC 7208:

  • 多層保護メカニズム
  • 明確な制限値
  • タイムアウト推奨
  • "void lookup"制限

B.5 IANA登録の変更​

B.5.1 SPF RRタイプ​

RFC 4408:

  • SPF RRタイプの登録(タイプ99)

RFC 7208:

  • SPF RRタイプを廃止としてマーク
  • TXTレコードのみを使用することを推奨

B.5.2 修飾子レジストリ​

RFC 7208新規:

  • SPF修飾子レジストリの作成
  • 新しい修飾子の標準化された登録プロセス
  • 未知の修飾子を無視する必要があるという要件

B.6 相互運用性の改善​

B.6.1 レコード選択の明確化​

RFC 4408:

  • 複数レコードの処理が不明確

RFC 7208:

  • 明確な規定: 複数のSPFレコードは"permerror"になる
  • バージョン文字列の一致ルールの改善
  • レコード連結ルールの明確化(複数の文字列)

B.6.2 エラー処理の標準化​

RFC 7208の改善:

  • すべてのエラー状況に明確な結果
  • DNS エラーの標準化された処理
  • タイムアウト処理の改善

B.7 下位互換性​

B.7.1 互換性のある変更​

ほとんどの変更は下位互換性があります:

  • TXTレコードフォーマットは変更なし
  • メカニズムと修飾子の構文は本質的に同じ
  • コアアルゴリズムは一貫性を保つ

B.7.2 互換性に影響を与える可能性のある変更​

  1. SPF RRタイプの削除:

    • 古い実装はまだSPF RRをクエリする可能性
    • 推奨: SPF RRレコードを削除し、TXTのみを保持
  2. より厳格な制限:

    • "void lookup"制限は新しく追加された
    • 一部の古いレコードは新しい制限を超える可能性
    • 推奨: 制限に準拠するようにSPFレコードを最適化
  3. "ptr"メカニズムへの反対:

    • まだサポートする必要があるが、強く非推奨
    • 推奨: 他のメカニズムに移行

B.8 ドキュメント構造の変更​

RFC 7208の改善:

  • より明確な章の構成
  • 拡張例の追加(付録A)
  • 実装推奨事項の追加(付録C-G)
  • ABNF定義の提示の改善

B.9 移行ガイド​

RFC 4408からRFC 7208への移行:​

公開者:

  1. SPF RRレコードを削除し、TXTレコードのみを保持
  2. DNSクエリ数を確認して最適化(≤ 10)
  3. "ptr"メカニズムを"ip4"または"ip6"に置き換える
  4. "void lookup"数を検証(≤ 2)
  5. レコードが512オクテットを超えるかテスト

受信者:

  1. SPF RRタイプのクエリを停止
  2. 新しいDNSクエリ制限を実装
  3. "void lookup"制限を実装
  4. エラー処理ロジックを更新
  5. 評価タイムアウトの実装を検討

テスト:

# SPF検証ツールを使用してレコードをテスト
# 例: https://www.kitterman.com/spf/validate.html

# DNSクエリ数を確認
dig +short example.com TXT | grep "v=spf1"

# レコードサイズを検証
dig example.com TXT | grep -A1 "ANSWER SECTION"

B.10 参照ドキュメントの更新​

RFC 7208は更新された標準を参照:

  • RFC 5321 (RFC 2821を置き換え)
  • RFC 5322 (RFC 2822を置き換え)
  • RFC 5234 (ABNF更新)
  • RFC 5598 (メールアーキテクチャ用語)
  • RFC 5890 (国際化ドメイン名)

Appendix C. Further Testing Advice (さらなるテストアドバイス)​

この付録は、SPF実装のテストと検証に関する詳細な推奨事項を提供します。

C.1 SPFレコードのテスト​

C.1.1 基本的な検証​

DNSクエリテスト:

# SPFレコードをクエリ
dig example.com TXT | grep "v=spf1"

# またはnslookupを使用
nslookup -type=TXT example.com

# またはhostを使用
host -t TXT example.com

検証ポイント:

  • ✓ レコードがv=spf1で始まる
  • ✓ SPFレコードは1つのみ
  • ✓ 構文が正しい(誤字がない)
  • ✓ レコードサイズ < 512オクテット

C.1.2 オンライン検証ツール​

推奨されるオンラインツール:

  1. Kitterman SPF Validator

    • URL: https://www.kitterman.com/spf/validate.html
    • 機能: 構文チェック、DNSクエリカウント、解析詳細
  2. MXToolbox SPF Record Check

    • URL: https://mxtoolbox.com/spf.aspx
    • 機能: レコード検証、警告とエラーの検出
  3. DMARCIAN SPF Inspector

    • URL: https://dmarcian.com/spf-survey/
    • 機能: 深層分析、最適化の推奨事項

チェック項目:

✓ 構文の正確性
✓ DNSクエリ数(≤ 10)
✓ Void lookup数(≤ 2)
✓ レコード長
✓ メカニズムの有効性
✓ Includeチェーンの深さ

C.2 SPFチェック実装のテスト​

C.2.1 ユニットテストシナリオ​

テストケース1: 基本的なIP一致

SPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass

テストIP: 192.0.2.2
期待される結果: fail

テストケース2: CIDR範囲

SPFレコード: v=spf1 ip4:192.0.2.0/24 -all
テストIP: 192.0.2.100
期待される結果: pass

テストIP: 192.0.3.1
期待される結果: fail

テストケース3: MXメカニズム

example.com MXレコード: 10 mail.example.com
mail.example.com Aレコード: 192.0.2.10

SPFレコード: v=spf1 mx -all
テストIP: 192.0.2.10
期待される結果: pass

テストケース4: Includeメカニズム

example.com: v=spf1 include:_spf.example.org -all
_spf.example.org: v=spf1 ip4:192.0.2.0/24 -all

テストIP: 192.0.2.50
期待される結果: pass

テストケース5: ソフトフェイル

SPFレコード: v=spf1 ip4:192.0.2.1 ~all
テストIP: 203.0.113.1
期待される結果: softfail

C.2.2 境界ケーステスト​

空のMAIL FROM:

MAIL FROM: <>
HELO: mail.example.com
期待: HELO識別を使用、local-partは"postmaster"

無効なドメイン名:

MAIL FROM: [email protected]
期待される結果: none (またはpermerror)

DNSタイムアウト:

DNSタイムアウトをシミュレート
期待される結果: temperror

複数のSPFレコード:

example.com TXT "v=spf1 mx -all"
example.com TXT "v=spf1 a -all"
期待される結果: permerror

C.2.3 制限テスト​

DNSクエリ制限テスト:

# 疑似コード
def test_dns_lookup_limit():
# 11個のincludeを含むSPFレコードを作成
spf = "v=spf1"
for i in range(11):
spf += f" include:domain{i}.example.com"
spf += " -all"

result = check_spf(spf, "192.0.2.1", "[email protected]")
assert result == "permerror"

MXレコード制限テスト:

# 11個のMXレコードを持つドメインを作成
# 期待: mxメカニズムがpermerrorを返す

Void Lookupテスト:

# NXDOMAINを返すincludeを作成
# カウントはvoid lookup制限に含まれるべき

C.3 テストメールの送信​

C.3.1 テストフロー​

ステップ1: テストドメインを準備

test.example.com IN TXT "v=spf1 ip4:YOUR_IP -all"

ステップ2: テストメールを送信

# swaksツールを使用
swaks --to [email protected] \
--from [email protected] \
--server smtp.example.com

# またはtelnetを使用
telnet smtp.recipient.com 25
HELO test.example.com
MAIL FROM:`&lt;[email protected]&gt;`
RCPT TO:`&lt;[email protected]&gt;`
DATA
Subject: SPF Test
.
QUIT

ステップ3: メールヘッダーを確認

Received-SPF: pass (recipient.com: domain of [email protected]
designates YOUR_IP as permitted sender)
client-ip=YOUR_IP;
[email protected];

C.3.2 異なるシナリオのテスト​

シナリオ1: Passテスト

設定: 承認されたIPから送信
期待されるヘッダー: Received-SPF: pass

シナリオ2: Failテスト

設定: 承認されていないIPから送信
期待: メールが拒否またはマーク
期待されるヘッダー: Received-SPF: fail

シナリオ3: SoftFailテスト

設定: SPFレコードが~allを使用
承認されていないIPから送信
期待: メールは受け入れられるがマーク
期待されるヘッダー: Received-SPF: softfail

シナリオ4: Neutralテスト

設定: SPFレコードが?allを使用
期待されるヘッダー: Received-SPF: neutral

C.4 マクロテスト​

C.4.1 マクロ展開の検証​

テストケース:

送信者: [email protected]
クライアントIP: 192.0.2.100

マクロ %\{s} → [email protected]
マクロ %\{l} → user
マクロ %\{o} → example.com
マクロ %\{d} → example.com
マクロ %\{i} → 192.0.2.100
マクロ %\{ir} → 100.2.0.192
マクロ %\{d2} → example.com
マクロ %\{d1} → com

複雑なマクロテスト:

SPF: v=spf1 exists:%\{ir}.%\{l}._spf.%\{d} -all
送信者: [email protected]
IP: 192.0.2.100

展開先: 100.2.0.192.user._spf.example.com
検証: このドメイン名のAレコードをクエリ

C.4.2 マクロ区切り文字テスト​

送信者: [email protected]

%\{l} → user+tag
%\{l-} → user-tag (+が-に置き換えられる)
%\{lr} → gat+resu (逆順)
%\{lr-} → gat-resu (逆順かつ置換)

C.5 パフォーマンステスト​

C.5.1 応答時間テスト​

ベンチマークテスト:

# DNSクエリ時間をテスト
time dig example.com TXT

# 完全なSPFチェックをテスト
time spf_check example.com 192.0.2.1 [email protected]

パフォーマンス目標:

  • シンプルなレコード(1-2メカニズム): < 100ms
  • 複雑なレコード(複数のinclude): < 500ms
  • 最大許容時間: 20秒

C.5.2 負荷テスト​

高並行性をシミュレート:

import concurrent.futures
import time

def check_spf_concurrent(num_checks):
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
futures = [
executor.submit(check_spf, "example.com", "192.0.2.1", "[email protected]")
for _ in range(num_checks)
]
results = [f.result() for f in futures]
return results

# 1000件の並行SPFチェックをテスト
start = time.time()
results = check_spf_concurrent(1000)
end = time.time()

print(f"1000件のチェックが完了: {end - start}秒")
print(f"平均1件あたり: {(end - start) / 1000 * 1000}ms")

C.6 リグレッションテスト​

C.6.1 テストスイート​

最小テストセット:

1. 基本的なpass/failシナリオ
2. すべての7つのメカニズム(all, include, a, mx, ptr, ip4, ip6, exists)
3. すべての4つの修飾子(+, -, ~, ?)
4. 両方の修飾子(redirect, exp)
5. マクロ展開
6. エラー処理(temperror, permerror)
7. 制限テスト(DNSクエリ、タイムアウト)

RFC 7208テストスイート:

# 公式テストスイートを使用
git clone https://github.com/openspf/openspf.git
cd openspf/tests
./run_tests.sh

C.6.2 継続的インテグレーション​

CI/CD設定例:

# .github/workflows/spf-tests.yml
name: SPF Tests

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run SPF Tests
run: |
python -m pytest tests/test_spf.py
python tests/validate_spf_records.py

C.7 一般的な問題のテスト​

C.7.1 古いメール機能​

ソースルーティング:

MAIL FROM:&lt;@relay.example.com:[email protected]>
期待: example.comをドメインとして正しく抽出

%-hack:

MAIL FROM:&lt;user%[email protected]>
期待: 正しく処理または拒否

Bang Path:

MAIL FROM:&lt;[email protected]>
期待: 正しく処理または拒否

C.7.2 国際化ドメイン名​

IDNテスト:

ドメイン: münchen.de
A-label: xn--mnchen-3ya.de
SPFレコード: A-labelを使用する必要がある

テスト: 正しい変換とクエリを確認

C.7.3 IPv6テスト​

IPv6アドレスフォーマット:

完全フォーマット: 2001:0db8:0000:0000:0000:0000:0000:0001
圧縮フォーマット: 2001:db8::1
IPv4マップ: ::ffff:192.0.2.1

SPF: v=spf1 ip6:2001:db8::/32 -all
テスト: すべてのフォーマットが正しく一致するべき

C.8 デバッグテクニック​

C.8.1 詳細ログを有効化​

送信者:

# Postfix
postconf -e "smtpd_sender_login_maps = hash:/etc/postfix/sender_login"
postconf -e "smtpd_sender_restrictions = reject_sender_login_mismatch"
tail -f /var/log/mail.log

受信者:

# SPFデバッグログを有効化
spf_debug_level = 5
tail -f /var/log/mail.log | grep SPF

C.8.2 digトレースを使用​

SPFクエリをトレース:

# 完全なDNS解決プロセスを表示
dig +trace example.com TXT

# Includeチェーンを表示
dig _spf.google.com TXT
dig _netblocks.google.com TXT

C.8.3 SPFデバッグツールを使用​

Python pyspf:

import spf

result, explanation = spf.check2(
i='192.0.2.1',
s='[email protected]',
h='mail.example.com'
)

print(f"結果: {result}")
print(f"説明: {explanation}")

Perl Mail::SPF:

use Mail::SPF;

my $spf_server = Mail::SPF::Server->new();
my $request = Mail::SPF::Request->new(
versions => [1],
scope => 'mfrom',
identity => '[email protected]',
ip_address => '192.0.2.1',
helo_identity => 'mail.example.com'
);

my $result = $spf_server->process($request);
print "結果: " . $result->code . "\n";

C.9 本番環境の監視​

C.9.1 監視メトリクス​

主要メトリクス:

- SPF passレート
- SPF failレート
- SPF softfailレート
- SPF temperrorレート(非常に低いはず)
- SPF permerrorレート(0であるべき)
- 平均チェック時間
- DNSタイムアウトレート

C.9.2 アラート設定​

推奨されるアラートルール:

- permerrorレート > 0%: 即座にアラート(SPFレコードエラー)
- temperrorレート > 5%: 警告(DNS問題)
- failレートの突然の上昇 > 20%: アラート(設定変更または攻撃の可能性)
- 平均チェック時間 > 1秒: 警告(パフォーマンス問題)

C.9.3 ログ分析​

SPF失敗を分析:

# SPF failのIPアドレスを抽出
grep "Received-SPF: fail" /var/log/mail.log | \
grep -oP 'client-ip=\K[0-9.]+' | \
sort | uniq -c | sort -rn

# failした送信者ドメインを分析
grep "Received-SPF: fail" /var/log/mail.log | \
grep -oP 'envelope-from=\K[^;]+' | \
cut -d@ -f2 | sort | uniq -c | sort -rn

レポートを生成:

# SPF統計レポートジェネレーター
def generate_spf_report(log_file):
results = {
'pass': 0, 'fail': 0, 'softfail': 0,
'neutral': 0, 'none': 0,
'temperror': 0, 'permerror': 0
}

with open(log_file) as f:
for line in f:
if 'Received-SPF:' in line:
for result in results:
if f'Received-SPF: {result}' in line:
results[result] += 1

total = sum(results.values())
for result, count in results.items():
percentage = (count / total * 100) if total > 0 else 0
print(f"{result}: {count} ({percentage:.2f}%)")

Appendix D. SPF/Mediator Interactions (SPF/メディエーター相互作用)​

この付録は、SPFとメールメディエーター(メーリングリストや転送サービスなど)との相互作用とその影響について議論します。

D.1 Originating ADMDs (発信元ADMD)​

発信元ADMDは、最初にメールを送信する管理ドメインです。メディエーターが存在する場合、発信元ADMDは、そのメールが通過する可能性のある転送およびリストサービスを考慮する必要があります。

D.1.1 問題の識別​

SPFの基本的な仮定:

  • メールは承認されたサーバーから受信者に直接送信される
  • MAIL FROM識別は変更されない
  • 送信IPアドレスはSPFレコードで承認されている

メディエーターによって壊される仮定:

  • メールは中間システムを経由する
  • 異なるIPアドレスから再送信される可能性がある
  • MAIL FROMは変更されるか、そのままになる可能性がある

D.1.2 送信者の選択肢​

オプション1: 緩いSPFポリシーを使用

example.com IN TXT "v=spf1 mx ~all"

利点:

  • 正当な転送を許可
  • 誤検知を減らす

欠点:

  • SPF保護を弱める
  • 偽造しやすくなる

オプション2: 他の認証方法に依存

DKIM署名を公開:
- DKIMは転送時も有効なまま
- 送信IPアドレスに依存しない
- DMARCと連携して機能

オプション3: SPF失敗を受け入れる

example.com IN TXT "v=spf1 mx -all"
  • 一部の正当なメールがマークまたは拒否される可能性を受け入れる
  • ユーザーが正しい送信方法を使用することに依存
  • ユーザーに転送ではなく「コピーとして送信」を使用することを推奨

D.1.3 ベストプラクティス​

推奨設定:

1. 厳格なSPFレコードを公開(-all)
2. 同時にDKIM署名を実装
3. DMARCポリシーを公開
4. 転送問題についてユーザーを教育

DMARC設定例:

_dmarc.example.com IN TXT "v=DMARC1; p=quarantine; sp=quarantine; adkim=r; aspf=r; pct=100; rua=mailto:[email protected]"

パラメータの説明:

  • adkim=r: DKIMリラックスドアライメント
  • aspf=r: SPFリラックスドアライメント
  • p=quarantine: 失敗時に隔離

D.2 Mediators (メディエーター)​

メディエーターは、送信チェーン内でメールを変更または再送信するシステムです。一般的なメディエーターには、メーリングリスト、転送サービス、自動応答システムが含まれます。

D.2.1 メーリングリスト​

問題:

従来のメーリングリストは元のMAIL FROMを保持します:

元: MAIL FROM:`&lt;[email protected]&gt;`
リスト後: MAIL FROM:`&lt;[email protected]&gt;`
送信IP: list-server.mailinglist.org

SPFチェック:

example.comのSPFレコードをクエリ
list-server.mailinglist.orgのIPをチェック
結果: fail(IPはexample.comのSPFレコードにない)

D.2.2 解決策​

解決策1: MAIL FROMを書き換える(推奨)

書き換え後: MAIL FROM:`&lt;[email protected]&gt;`
Reply-To: [email protected]

利点:

  • SPFチェックが成功
  • バウンスはリストサーバーに戻る
  • 受信者が正しく識別できる

欠点:

  • 元の送信者情報を変更
  • ユーザーがReply-Toに適応する必要がある

実装例(Mailman):

# Mailman設定
from_is_list = 2 # Munge From, add Reply-To

解決策2: SRS (Sender Rewriting Scheme)を使用

SRSはより複雑な書き換えスキームです:

SRS形式:

SRS0=&lt;hash>=&lt;timestamp>=&lt;domain>=&lt;localpart>@&lt;forwarder>

例:
[email protected]

利点:

  • 元の送信者情報を保持(local-part内)
  • SPFチェックが成功
  • 追跡および検証可能

欠点:

  • 実装が複雑
  • SRSデータベースが必要
  • local-partが読みにくくなる

解決策3: SPFではなくDKIMを使用

設定:

1. 元の送信者がDKIM署名を使用
2. リストサーバーが元の署名を保持
3. リストサーバーが独自のDKIM署名を追加
4. 受信者がDKIMを検証(SPF失敗を無視)

DMARC設定:

_dmarc.example.com IN TXT "v=DMARC1; p=none; adkim=r; aspf=r"
  • p=none: 強制なし(SPF失敗を許可)
  • adkim=r: DKIMリラックスドアライメント(リスト署名を許可)

D.2.3 メール転送サービス​

タイプ1: シンプル転送

ユーザー設定: [email protected] → [email protected]
転送サービスが保持: MAIL FROM:`&lt;[email protected]&gt;`
転送サーバーのIPから送信

SPF問題:

Gmailはoriginal.comのSPFをチェック
転送サーバーのIPはSPFレコードにない
結果: fail

解決策A: SRS書き換え

転送サービスがSRSを使用:
MAIL FROM:&lt;[email protected]>

解決策B: -allの代わりに~allを使用

original.com IN TXT "v=spf1 mx ~all"

転送されたメールはfailではなくsoftfailを受け取り、受け入れられる可能性が高くなります。

タイプ2: .forwardファイル

# ~/.forward
[email protected]

問題はシンプル転送と同じ。

ベストプラクティス:

1. 転送サービスはSRSを実装すべき
2. 元の送信者はDKIMを使用すべき
3. 受信者はDKIM結果を考慮すべきで、SPFだけではない

D.2.4 自動応答および通知システム​

問題:

元のメール: [email protected] → [email protected]
自動応答: MAIL FROM:`&lt;[email protected]&gt;`
(company.comサーバーから送信)

SPFチェックは失敗します。

解決策:

空のMAIL FROMで通知を送信:
MAIL FROM:<>
From: Mail Delivery System `&lt;[email protected]&gt;`

または:

ローカルアドレスを使用:
MAIL FROM:`&lt;[email protected]&gt;`
From: Automatic Reply `&lt;[email protected]&gt;`
Reply-To: [email protected]

D.3 Receiving ADMDs (受信ADMD)​

受信ADMDは、メディエーターからのメールを処理し、適切な決定を下す必要があります。

D.3.1 メディエーターメールの識別​

識別子:

1. Receivedヘッダーの中間ホップ
2. List-*ヘッダー(メーリングリスト)
3. Precedence: bulkまたはlist
4. SRS形式のMAIL FROM
5. 既知のリストサービスからのDKIM署名

検出ロジックの例:

def is_mailing_list(message):
# List-*ヘッダーをチェック
list_headers = [
'List-Id', 'List-Post', 'List-Unsubscribe',
'List-Help', 'List-Subscribe', 'List-Owner'
]

for header in list_headers:
if message.get(header):
return True

# Precedenceをチェック
if message.get('Precedence') in ['bulk', 'list']:
return True

# SRSをチェック
mail_from = message.get('Return-Path', '')
if mail_from.startswith('SRS0=') or mail_from.startswith('SRS1='):
return True

return False

D.3.2 ポリシー推奨事項​

ポリシー1: リストメールに対する寛大な取り扱い

if is_mailing_list(message):
if spf_result == 'fail':
# DKIMをチェック
if dkim_result == 'pass':
accept_message()
else:
# リストの評判をチェック
if list_is_trusted(list_id):
accept_message()
else:
quarantine_message()

ポリシー2: DMARCに依存

if dmarc_result == 'pass':
accept_message()
elif spf_result == 'fail' and dkim_result == 'fail':
reject_message()
else:
# SPFまたはDKIMのいずれかが成功
if is_mailing_list(message):
accept_message()
else:
apply_content_filtering()

ポリシー3: ユーザー固有のルール

ユーザーはホワイトリストを設定できます:
- 信頼できるメーリングリスト
- 既知の転送アドレス
- 特定ドメインの寛大な取り扱い

D.3.3 実装推奨事項​

SpamAssassin設定:

# メーリングリストに対する寛大な取り扱い
header LIST_ID exists:List-Id
score LIST_ID -0.1

# SPF failだがList-Idヘッダーあり
meta SPF_FAIL_LIST (SPF_FAIL && LIST_ID)
score SPF_FAIL_LIST 0.5

# 通常のSPF fail(リストヘッダーなし)
score SPF_FAIL 5.0

Postfix設定:

# smtpd_recipient_restrictions
reject_unauth_destination,
check_policy_service unix:private/spf-policy,
permit

# SPFポリシーサービスはリストメールを識別して調整可能

rspamd設定:

-- SPFモジュール設定
spf {
-- メーリングリストに異なる重みを適用
symbol_fail = "R_SPF_FAIL";
symbol_softfail = "R_SPF_SOFTFAIL";

-- カスタムルール
custom_symbols = {
R_SPF_FAIL_LIST = {
score = 2.0,
description = "SPF failed but from mailing list",
condition = function(task)
local spf = task:get_symbol('R_SPF_FAIL')
local list_id = task:get_header('List-Id')
return spf and list_id
end
}
}
}

D.4 相互運用性の推奨事項​

D.4.1 標準化​

標準ヘッダーを使用:

メーリングリストは追加すべき:
- List-Id: &lt;list-name.domain.com>
- List-Post: &lt;mailto:[email protected]>
- List-Unsubscribe: &lt;mailto:[email protected]>
- Precedence: bulk

DKIM署名:

リストサーバーは:
1. 元のDKIM署名を保持(存在する場合)
2. 独自のDKIM署名を追加
3. 署名済みヘッダーを変更しない

D.4.2 コミュニケーション​

送信者への通知:

リストサービスは購読者に通知すべき:
- メールはリストサーバーから再送信される
- MAIL FROMは書き換えられる
- リストに返信するためにReply-Toを使用することを推奨

受信者ドキュメント:

受信ポリシーをドキュメント化:
- リストメールの処理方法
- ホワイトリストメカニズム
- ユーザーが設定可能なオプション

D.5 将来の開発方向​

D.5.1 ARC (Authenticated Received Chain)​

RFC 8617がARCを導入:

ARCはメディエーターが認証情報を追加することを許可:
- ARC-Seal: メディエーターの署名
- ARC-Message-Signature: メッセージ署名
- ARC-Authentication-Results: 認証結果

例:

ARC-Seal: i=1; a=rsa-sha256; t=1234567890; cv=none;
d=mailinglist.org; s=arc-seal; b=...
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed;
d=mailinglist.org; s=arc-msg; h=from:to:subject; b=...
ARC-Authentication-Results: i=1; mailinglist.org;
spf=pass [email protected];
dkim=pass header.d=example.com

利点:

  • 元の認証結果を保持
  • チェーン検証を可能にする
  • 受信者はメディエーターを信頼できる

D.5.2 推奨事項​

新規デプロイメントの場合:

  1. DKIMとSPFを実装
  2. DMARCポリシーを公開
  3. ARCサポートを検討(メディエーターの場合)
  4. 一般的なメーリングリストとの相互運用性をテスト

既存システムの場合:

  1. SPFポリシーの厳格さを見直す
  2. DMARCレポートを監視
  3. 問題のある送信元を識別(リスト、転送など)
  4. ポリシーを調整するか、SRSを実装

Appendix E. Mail Services (メールサービス)​

この付録では、さまざまなメールサービスシナリオのSPF実装に関する考慮事項について説明します。

E.1 Web-Based Mail Services (Webベースのメールサービス)​

E.1.1 サービスプロバイダーの視点​

シナリオ: Webメールサービスの提供(Gmail、Yahoo Mail、Outlook.comなど)

SPFの考慮事項:

1. ユーザーは複数のドメインからメールを送信する
2. すべてのメールはサービスプロバイダーのサーバーを経由して送信される
3. 適切なSPFレコードが必要

設定例:

# サービスプロバイダーのメインドメイン
mailprovider.com IN TXT "v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.0/24 -all"

# カスタムドメインのサポート
# ユーザーは自分のドメインにサービスプロバイダーを含める必要がある
user-domain.com IN TXT "v=spf1 include:_spf.mailprovider.com -all"
_spf.mailprovider.com IN TXT "v=spf1 ip4:203.0.113.0/24 ip4:198.51.100.0/24 -all"

E.1.2 カスタムドメインの設定​

ユーザー要件: Webメールサービス経由でメールを送信するためにカスタムドメインを使用する

設定手順:

  1. ドメイン所有権の検証
サービスプロバイダーは検証レコードの追加を要求する:
_verification.user-domain.com IN TXT "provider-verification-code"
  1. SPFの設定
user-domain.com IN TXT "v=spf1 include:_spf.mailprovider.com -all"
  1. DKIMの設定
# サービスプロバイダーがDKIM公開鍵を提供
selector._domainkey.user-domain.com IN TXT "v=DKIM1; k=rsa; p=MIGfMA0..."
  1. DMARCの設定
_dmarc.user-domain.com IN TXT "v=DMARC1; p=quarantine; rua=mailto:[email protected]"

E.2 Shared Hosting Services (共有ホスティングサービス)​

E.2.1 課題​

問題: 複数の顧客が同じIPアドレスを共有

hosting-provider.com: 203.0.113.10
customer1.com: 203.0.113.10でホスト
customer2.com: 203.0.113.10でホスト
customer3.com: 203.0.113.10でホスト

SPF要件: 各顧客ドメインは共有IPを承認する必要がある

E.2.2 ソリューション​

ソリューション1: 統一SPFインクルード

# ホスティングプロバイダーが提供
_spf.hosting-provider.com IN TXT "v=spf1 ip4:203.0.113.0/24 -all"

# 顧客の設定
customer1.com IN TXT "v=spf1 include:_spf.hosting-provider.com -all"
customer2.com IN TXT "v=spf1 include:_spf.hosting-provider.com -all"
customer3.com IN TXT "v=spf1 include:_spf.hosting-provider.com -all"

ソリューション2: 直接IP承認

# 各顧客が独立して設定
customer1.com IN TXT "v=spf1 ip4:203.0.113.10 -all"
customer2.com IN TXT "v=spf1 ip4:203.0.113.10 -all"

利点: シンプル、依存関係なし 欠点: IP変更時、すべての顧客が更新する必要がある

ソリューション3: 専用IP(大口顧客に推奨)

# 重要な顧客に専用IPを割り当てる
important-customer.com IN TXT "v=spf1 ip4:203.0.113.50 -all"

E.2.3 ベストプラクティス​

ホスティングプロバイダーがすべきこと:

1. 明確なSPF設定ドキュメントを提供する
2. SPFレコードを自動生成する(コントロールパネル経由)
3. IPアドレスの変更を顧客に通知する
4. SPF検証ツールを提供する

顧客がすべきこと:

1. includeメソッドを使用してホスティングプロバイダーのSPFを参照する
2. SPFレコードを定期的に検証する
3. メール配信性を監視する

E.3 Enterprise Mail Systems (エンタープライズメールシステム)​

E.3.1 複雑な環境​

典型的な企業シナリオ:

- 複数のデータセンター
- 複数のアウトバウンドメールサーバー
- サードパーティメールサービス(マーケティング、通知など)
- ローカルオフィスサーバー
- クラウドサービス統合

SPF設定の課題:

1. DNS照会制限(10回)
2. レコードサイズ制限(512バイト)
3. 複数の部門/事業単位
4. 頻繁なインフラストラクチャの変更

E.3.2 エンタープライズSPFアーキテクチャ​

階層設計:

# メインドメイン
company.com IN TXT "v=spf1 include:_spf-internal.company.com include:_spf-external.company.com -all"

# 内部サーバー
_spf-internal.company.com IN TXT "v=spf1 ip4:10.0.0.0/8 ip4:203.0.113.0/24 -all"

# 外部サービス
_spf-external.company.com IN TXT "v=spf1 include:_spf.salesforce.com include:sendgrid.net include:mailchimp.com -all"

# 部門サブドメイン
marketing.company.com IN TXT "v=spf1 include:_spf-marketing.company.com -all"
_spf-marketing.company.com IN TXT "v=spf1 include:sendgrid.net include:mailchimp.com -all"

IP範囲管理:

# データセンターA
_spf-dc-a.company.com IN TXT "v=spf1 ip4:203.0.113.0/25 -all"

# データセンターB
_spf-dc-b.company.com IN TXT "v=spf1 ip4:198.51.100.0/25 -all"

# 集約
_spf-datacenters.company.com IN TXT "v=spf1 include:_spf-dc-a.company.com include:_spf-dc-b.company.com -all"

E.3.3 SPFフラットニング戦略​

問題: 10回のDNS照会制限を超える

ソリューション: 定期的なフラットニング

# 自動化スクリプト
def flatten_spf_includes():
"""
すべてのインクルードドメインのIPを定期的に照会
フラット化されたSPFレコードを生成
"""
includes = [
'_spf.salesforce.com',
'sendgrid.net',
'mailchimp.com'
]

ips = []
for domain in includes:
# SPFレコードを解析してIPを抽出
ips.extend(resolve_spf_ips(domain))

# 新しいSPFレコードを生成
spf_record = f"v=spf1 {' '.join([f'ip4:{ip}' for ip in ips])} -all"

# DNSを更新(API経由)
update_dns_record('_spf-flattened.company.com', spf_record)

# 週次で実行

注意: フラットニング後は定期的に更新する必要がある(サードパーティのIPが変更される可能性があるため)

E.4 Email Marketing Services (メールマーケティングサービス)​

E.4.1 サービス設定​

一般的なサービス:

  • SendGrid
  • Mailchimp
  • Amazon SES
  • Mailgun

SPF設定例:

SendGrid:

company.com IN TXT "v=spf1 include:sendgrid.net -all"

Mailchimp:

company.com IN TXT "v=spf1 include:servers.mcsv.net -all"

Amazon SES:

company.com IN TXT "v=spf1 include:amazonses.com -all"

組み合わせ使用:

company.com IN TXT "v=spf1 mx include:sendgrid.net include:servers.mcsv.net -all"

E.4.2 サブドメイン戦略​

ベストプラクティス: マーケティングメール用に専用サブドメインを使用

# メインドメイン(重要なビジネスメール用)
company.com IN TXT "v=spf1 mx -all"

# マーケティングサブドメイン
marketing.company.com IN TXT "v=spf1 include:sendgrid.net -all"

# 通知サブドメイン
notifications.company.com IN TXT "v=spf1 include:amazonses.com -all"

利点:

1. レピュテーションの分離(マーケティングメールがメインドメインに影響しない)
2. SPFレコード管理が簡単
3. より良い監視とレポート
4. DMARCベストプラクティスに準拠

E.4.3 DKIM設定​

SPFと組み合わせ:

# SPF
marketing.company.com IN TXT "v=spf1 include:sendgrid.net -all"

# DKIM(SendGridが提供)
s1._domainkey.marketing.company.com IN CNAME s1.domainkey.u12345.wl.sendgrid.net
s2._domainkey.marketing.company.com IN CNAME s2.domainkey.u12345.wl.sendgrid.net

# DMARC
_dmarc.marketing.company.com IN TXT "v=DMARC1; p=quarantine; pct=100; rua=mailto:[email protected]"

E.5 Transactional Email Services (トランザクションメールサービス)​

E.5.1 シナリオ​

トランザクションメール:

  • 登録確認
  • パスワードリセット
  • 注文通知
  • 請求書と領収書

サービスプロバイダー:

  • Amazon SES
  • SendGrid
  • Mailgun
  • Postmark

E.5.2 設定​

専用サブドメインを使用:

# トランザクションメールサブドメイン
transactional.company.com IN TXT "v=spf1 include:_spf.mailgun.org -all"

# DKIM
smtp._domainkey.transactional.company.com IN TXT "v=DKIM1; k=rsa; p=..."

アプリケーション設定:

# Django例
EMAIL_HOST = 'smtp.mailgun.org'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_FROM = '[email protected]'

# メール送信
send_mail(
subject='Password Reset',
message='Click here to reset...',
from_email='[email protected]',
recipient_list=['[email protected]']
)

E.5.3 監視​

主要メトリクス:

- メール配信率
- バウンス率
- SPF合格率
- DKIM合格率
- DMARCアライメント率
- 苦情率

アラート設定:

if delivery_rate &lt; 95%:
alert("Delivery rate dropped below 95%")

if spf_pass_rate &lt; 98%:
alert("SPF configuration issue detected")

E.6 Mobile and IoT Devices (モバイルおよびIoTデバイス)​

E.6.1 課題​

モバイルデバイス:

- 動的IPアドレス
- キャリアネットワーク経由で送信
- SPFにリストできない

IoTデバイス:

- 大量のデバイス
- さまざまなネットワークに分散
- 直接メール送信

E.6.2 ソリューション​

ソリューション1: SMTPリレーを使用

デバイス → SMTPリレー → 受信者

設定:
- デバイスは認証を通じてリレーに接続
- リレーサーバーがSPFレコードで承認される
- リレーがDKIM署名を追加

SPF設定:

iot-devices.company.com IN TXT "v=spf1 mx include:_spf-relay.company.com -all"
_spf-relay.company.com IN TXT "v=spf1 ip4:203.0.113.50 -all"

ソリューション2: API送信

デバイスはHTTP API経由でメールを送信:
- サードパーティサービスを使用(SendGrid APIなど)
- サービスプロバイダーのSPFレコードでカバー
- デバイスからの直接SMTP送信不要

E.7 Best Practices Summary (ベストプラクティス要約)​

E.7.1 一般的な推奨事項​

1. さまざまなメールタイプを分離するためにサブドメインを使用
2. SPF + DKIM + DMARCの三重保護を実装
3. SPFレコードを定期的に確認および更新
4. DNS照会制限を監視
5. すべてのIPを直接リストする代わりにincludeを使用
6. 重要なサービスには専用IPを使用
7. メール認証監視を実装
8. インシデント対応プロセスを確立

E.7.2 設定チェックリスト​

公開前に確認:

□ SPFレコード構文が正しい
□ DNS照会回数 ≤ 10
□ レコードサイズ &lt; 512バイト
□ すべての送信サーバーが承認されている
□ 適切な修飾子を使用(-allまたは~all)
□ DKIMが設定されている
□ DMARCポリシーが公開されている
□ テストメールが送信および検証されている

継続的な監視:

□ DMARCレポートを週次で確認
□ メール配信性を監視
□ SPF/DKIMエラーを追跡
□ 新しい送信元を確認
□ サードパーティサービスの変更を更新

Appendix F. Test Suite (テストスイート)​

この付録は、SPF実装を検証するための包括的なテストスイートを提供します。テストは、SPFチェッカーが正しく動作することを確認するためのさまざまなシナリオをカバーしています。

F.1 Basic Tests (基本テスト)​

F.1.1 シンプルなIP4テスト​

テスト1: 直接IP4マッチ

SPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass

テスト2: IP4 CIDRマッチ

SPFレコード: v=spf1 ip4:192.0.2.0/24 -all
テストIP: 192.0.2.128
期待される結果: pass

テスト3: IP4非マッチ

SPFレコード: v=spf1 ip4:192.0.2.0/24 -all
テストIP: 192.0.3.1
期待される結果: fail

F.1.2 シンプルなIP6テスト​

テスト4: 直接IP6マッチ

SPFレコード: v=spf1 ip6:2001:db8::1 -all
テストIP: 2001:db8::1
期待される結果: pass

テスト5: IP6 CIDRマッチ

SPFレコード: v=spf1 ip6:2001:db8::/32 -all
テストIP: 2001:db8::dead:beef
期待される結果: pass

テスト6: IP6非マッチ

SPFレコード: v=spf1 ip6:2001:db8::/32 -all
テストIP: 2001:db9::1
期待される結果: fail

F.2 Mechanism Tests (メカニズムテスト)​

F.2.1 Aメカニズムテスト​

テスト7: Aメカニズム - マッチ

ドメイン: example.com
SPFレコード: v=spf1 a -all
example.comのDNS Aレコード: 192.0.2.1
テストIP: 192.0.2.1
期待される結果: pass

テスト8: CIDRを含むAメカニズム

ドメイン: example.com
SPFレコード: v=spf1 a/24 -all
example.comのDNS Aレコード: 192.0.2.1
テストIP: 192.0.2.200
期待される結果: pass

テスト9: ドメイン指定を含むAメカニズム

ドメイン: sender.example.com
SPFレコード: v=spf1 a:mail.example.com -all
mail.example.comのDNS Aレコード: 192.0.2.10
テストIP: 192.0.2.10
期待される結果: pass

F.2.2 MXメカニズムテスト​

テスト10: MXメカニズム - マッチ

ドメイン: example.com
SPFレコード: v=spf1 mx -all
example.comのDNS MXレコード: 10 mail.example.com
mail.example.comのDNS Aレコード: 192.0.2.1
テストIP: 192.0.2.1
期待される結果: pass

テスト11: 複数のMXレコードを含むMXメカニズム

ドメイン: example.com
SPFレコード: v=spf1 mx -all
DNS MXレコード:
10 mail1.example.com → 192.0.2.1
20 mail2.example.com → 192.0.2.2
テストIP: 192.0.2.2
期待される結果: pass

テスト12: CIDRを含むMXメカニズム

ドメイン: example.com
SPFレコード: v=spf1 mx/24 -all
DNS MXレコード: 10 mail.example.com
mail.example.comのDNS Aレコード: 192.0.2.1
テストIP: 192.0.2.200
期待される結果: pass

F.2.3 PTRメカニズムテスト (使用しない)​

テスト13: PTRメカニズム - マッチ

ドメイン: example.com
SPFレコード: v=spf1 ptr -all
テストIP: 192.0.2.1
192.0.2.1のPTRレコード: mail.example.com
mail.example.comのAレコード: 192.0.2.1
期待される結果: pass

注意: PTRメカニズムは非推奨であり、使用すべきではありません。

F.2.4 EXISTSメカニズムテスト​

テスト14: EXISTSメカニズム - 見つかった

ドメイン: example.com
SPFレコード: v=spf1 exists:%\{i}.whitelist.example.com -all
テストIP: 192.0.2.1
192.0.2.1.whitelist.example.comのDNS A照会: 127.0.0.2
期待される結果: pass

テスト15: EXISTSメカニズム - 見つからない

ドメイン: example.com
SPFレコード: v=spf1 exists:%\{i}.whitelist.example.com -all
テストIP: 192.0.2.99
192.0.2.99.whitelist.example.comのDNS A照会: NXDOMAIN
期待される結果: fail

F.2.5 INCLUDEメカニズムテスト​

テスト16: INCLUDE - シンプルなpass伝播

ドメイン: example.com
SPFレコード: v=spf1 include:trusted.example.com -all
trusted.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass

テスト17: INCLUDE - failは伝播しない

ドメイン: example.com
SPFレコード: v=spf1 include:trusted.example.com -all
trusted.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.99
期待される結果: fail (includeからではなく-allから)

テスト18: INCLUDE - TempError伝播

ドメイン: example.com
SPFレコード: v=spf1 include:trusted.example.com -all
trusted.example.comのDNS照会: DNSサーバー利用不可
期待される結果: temperror

テスト19: INCLUDE - PermError伝播

ドメイン: example.com
SPFレコード: v=spf1 include:trusted.example.com -all
trusted.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 ip4:192.0.2.1 -all (無効)
期待される結果: permerror

F.2.6 ALLメカニズムテスト​

テスト20: 異なる修飾子を持つALL

+all: pass
-all: fail
~all: softfail
?all: neutral

F.3 Qualifier Tests (修飾子テスト)​

テスト21: プラス修飾子 (+)

SPFレコード: v=spf1 +ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass

テスト22: マイナス修飾子 (-)

SPFレコード: v=spf1 -ip4:192.0.2.1 ~all
テストIP: 192.0.2.1
期待される結果: fail

テスト23: チルダ修飾子 (~)

SPFレコード: v=spf1 ~ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: softfail

テスト24: クエスチョンマーク修飾子 (?)

SPFレコード: v=spf1 ?ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: neutral

F.4 Modifier Tests (モディファイアテスト)​

F.4.1 REDIRECTモディファイアテスト​

テスト25: REDIRECT - 基本的なリダイレクト

ドメイン: example.com
SPFレコード: v=spf1 redirect=_spf.example.com
_spf.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass

テスト26: REDIRECT - メカニズム後は無視される

ドメイン: example.com
SPFレコード: v=spf1 ip4:192.0.2.1 redirect=_spf.example.com
テストIP: 192.0.2.1
期待される結果: pass (メカニズムがマッチ、redirectは無視)

テスト27: REDIRECT - マッチなし

ドメイン: example.com
SPFレコード: v=spf1 redirect=_spf.example.com
_spf.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.99
期待される結果: fail

F.4.2 EXPモディファイアテスト​

テスト28: EXP - 説明文字列の取得

ドメイン: example.com
SPFレコード: v=spf1 ip4:192.0.2.1 -all exp=explain.example.com
explain.example.comのDNS TXTレコード: "Mail from %\{i} not allowed"
テストIP: 192.0.2.99
期待される結果: fail
期待される説明: "Mail from 192.0.2.99 not allowed"

F.5 Macro Tests (マクロテスト)​

F.5.1 基本的なマクロ展開​

テスト29: %\{s} - 送信者

ドメイン: example.com
MAIL FROM: [email protected]
SPFレコード: v=spf1 exists:%\{s}.whitelist.example.com -all
展開先: exists:[email protected]

テスト30: %\{l} - ローカル部分

ドメイン: example.com
MAIL FROM: [email protected]
SPFレコード: v=spf1 exists:%\{l}.whitelist.example.com -all
展開先: exists:sender.whitelist.example.com

テスト31: %\{o} - ドメイン

ドメイン: example.com
MAIL FROM: [email protected]
SPFレコード: v=spf1 exists:%\{o}.whitelist.example.com -all
展開先: exists:example.com.whitelist.example.com

テスト32: %\{d} - 現在のドメイン

ドメイン: mail.example.com
SPFレコード: v=spf1 exists:%\{d}.whitelist.example.com -all
展開先: exists:mail.example.com.whitelist.example.com

テスト33: %\{i} - IPアドレス

テストIP: 192.0.2.1
SPFレコード: v=spf1 exists:%\{i}.whitelist.example.com -all
展開先: exists:192.0.2.1.whitelist.example.com

テスト34: %\{i} - IPv6アドレス

テストIP: 2001:db8::1
SPFレコード: v=spf1 exists:%\{i}.whitelist.example.com -all
展開先: exists:2001.0db8.0000.0000.0000.0000.0000.0001.whitelist.example.com

F.5.2 マクロトランスフォーマー​

テスト35: リバーストランスフォーマー (r)

ドメイン: mail.example.com
マクロ: %\{d}
通常: mail.example.com
rを使用: %\{dr}
展開先: com.example.mail

テスト36: 区切り文字とリバース

MAIL FROM: [email protected]
マクロ: %\{or}
展開先: com.example.mail

テスト37: 数字指定

ドメイン: mail.example.com
マクロ: %\{d2}
展開先: example.com (最後の2コンポーネント)

テスト38: リバース付き数字

ドメイン: mail.example.com
マクロ: %\{d2r}
展開先: com.example

F.5.3 マクロでのURLエンコーディング​

テスト39: URLエスケープ文字

MAIL FROM: "test user"@example.com
マクロ: %\{l}
展開先: test%20user

F.6 DNS Lookup Limit Tests (DNS照会制限テスト)​

テスト40: DNS照会制限 - 正確に10

SPFレコード: v=spf1 mx a include:d1.example.com include:d2.example.com ...
(DNS照会を必要とする合計10個のメカニズム)
期待される結果: 正常に処理される

テスト41: DNS照会制限 - 11で超過

SPFレコード: v=spf1 mx a include:d1.example.com ... (11照会)
期待される結果: permerror

テスト42: ネストされたinclude照会カウント

ドメイン: example.com
SPF: v=spf1 include:a.example.com -all (1照会)
a.example.com: v=spf1 include:b.example.com -all (1照会)
b.example.com: v=spf1 mx a -all (2照会)
合計照会: 4
期待される結果: pass (IPがマッチする場合)

F.7 Void Lookup Tests (空の照会テスト)​

テスト43: Aメカニズム - Aレコードなし

ドメイン: example.com
SPFレコード: v=spf1 a -all
example.comのDNS Aレコード: (なし)
テストIP: 192.0.2.1
期待される結果: fail (メカニズムがマッチしない)
空の照会: 1

テスト44: MXメカニズム - MXレコードなし

ドメイン: example.com
SPFレコード: v=spf1 mx -all
example.comのDNS MXレコード: (なし)
テストIP: 192.0.2.1
期待される結果: fail
空の照会: 1

テスト45: 空の照会制限 - 超過

それぞれが空の照会を返す3つのメカニズムを持つSPFレコード
期待される結果: permerror (制限が2の場合)

F.8 Syntax Error Tests (構文エラーテスト)​

テスト46: SPFバージョンなし

SPFレコード: ip4:192.0.2.1 -all
期待される結果: none (有効なSPFレコードなし)

テスト47: 無効なIPアドレス

SPFレコード: v=spf1 ip4:999.999.999.999 -all
期待される結果: permerror

テスト48: 無効なCIDR範囲

SPFレコード: v=spf1 ip4:192.0.2.0/99 -all
期待される結果: permerror

テスト49: 重複したモディファイア

SPFレコード: v=spf1 redirect=a.example.com redirect=b.example.com
期待される結果: permerror

テスト50: 未知のモディファイア (無視されるべき)

SPFレコード: v=spf1 ip4:192.0.2.1 unknown=value -all
テストIP: 192.0.2.1
期待される結果: pass (未知のモディファイアは無視)

F.9 Integration Tests (統合テスト)​

テスト51: 複雑な現実的シナリオ

ドメイン: company.com
SPFレコード: v=spf1 mx include:_spf.google.com include:sendgrid.net -all

設定:
- MXレコード: mail1.company.com (192.0.2.1), mail2.company.com (192.0.2.2)
- GoogleのSPFは複数のIP範囲を含む
- SendGridのSPFは複数のIP範囲を含む

テストケース:
1. IP 192.0.2.1: pass (mxマッチ)
2. Google範囲からのIP: pass (includeマッチ)
3. SendGrid範囲からのIP: pass (includeマッチ)
4. ランダムIP: fail (-all)

テスト52: サブドメイン処理

ドメイン: mail.company.com
company.comのSPFレコード: v=spf1 -all
mail.company.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass (サブドメインには独自のSPFレコードがある)

テスト53: SPFレコードなし

ドメイン: example.com
SPFレコード: (なし)
テストIP: 192.0.2.1
期待される結果: none

F.10 Edge Cases (エッジケース)​

テスト54: 空のローカル部分

MAIL FROM: <>
HELO: mail.example.com
mail.example.comのSPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass (HELOアイデンティティを使用)

テスト55: IPv4マップIPv6アドレス

SPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: ::ffff:192.0.2.1 (IPv4マップIPv6)
期待される結果: pass (IPv4として扱われるべき)

テスト56: IPv4の/32 CIDR

SPFレコード: v=spf1 ip4:192.0.2.1/32 -all
テストIP: 192.0.2.1
期待される結果: pass

テスト57: IPv6の/128 CIDR

SPFレコード: v=spf1 ip6:2001:db8::1/128 -all
テストIP: 2001:db8::1
期待される結果: pass

テスト58: 複数のSPFレコード (無効)

ドメイン: example.com
SPFレコード:
v=spf1 ip4:192.0.2.1 -all
v=spf1 ip4:192.0.2.2 -all
期待される結果: permerror (複数のSPFレコードは許可されない)

F.11 Performance Tests (パフォーマンステスト)​

テスト59: 最大DNS照会深度

深度10までのネストされたinclude
すべてが正しく処理されることを確認

テスト60: 大きなCIDR範囲

SPFレコード: v=spf1 ip4:192.0.0.0/8 -all
テストIP: 192.255.255.255
期待される結果: pass

F.12 Test Result Summary Format (テスト結果サマリー形式)​

各テスト実行について、以下の情報を記録する必要があります:

テストID: F.1.1-Test1
説明: 直接IP4マッチ
SPFレコード: v=spf1 ip4:192.0.2.1 -all
テストIP: 192.0.2.1
期待される結果: pass
実際の結果: pass
ステータス: 合格
DNS照会: 1
空の照会: 0
処理時間: 45ms

完全なSPF実装は、RFC 7208に準拠していると見なされるために、このスイートのすべてのテストに合格する必要があります。


Appendix G. Acknowledgements (謝辞)​

Sender Policy Framework (SPF)の開発は、インターネットコミュニティの多くの個人および組織が関与した共同作業でした。

G.1 Original Contributors (元の貢献者)​

SPFは、いくつかの以前のメール認証提案のアイデアに基づいています:

  • Designated Mailers Protocol (DMP): Gordon Fecykによって提案
  • Reverse MX (RMX): Hadmut Danischによって提案
  • Sender Permitted From (SPF): Meng Weng Wongによって提案

Meng Weng Wongは、これらのさまざまなアプローチを統一されたフレームワークに統合する上で重要な役割を果たし、SPFの初期開発を主導しました。

G.2 RFC 4408 Contributors (RFC 4408貢献者)​

元のSPF仕様であるRFC 4408は、Meng Weng WongとMark Lentcznerによって執筆されました。多くの他の人々がその開発に貢献しました:

技術貢献者およびレビュアー:

  • Stuart Cheshire
  • Claus Färber
  • Mark Lentczner
  • Wayne Schlitt
  • Julian Mehnle
  • Scott Kitterman

ワーキンググループ参加者: IETF MARID (MTA Authorization Records in DNS)ワーキンググループは、SPF概念の初期開発と議論に重要な貢献を行いました。

G.3 RFC 7208 Contributors (RFC 7208貢献者)​

この現在の仕様(RFC 7208)は、Scott KittermanによってRFC 4408に基づいて執筆されました。以下の人々がこの更新に貢献しました:

主要貢献者:

  • Scott Kitterman (主著者)
  • Murray S. Kucherawy (貢献者およびレビュアー)

技術レビュアーおよび貢献者:

  • Alessandro Vesely
  • Barry Leiba
  • Bron Gondwana
  • Franck Martin
  • John Levine
  • Michael Hammer
  • Pete Resnick
  • Steve Atkins
  • Stuart Cheshire

G.4 Community Contributions (コミュニティの貢献)​

SPFコミュニティは、SPFの開発、実装、展開に積極的に貢献してきました:

実装者: 多数の開発者がさまざまなプログラミング言語でSPFライブラリとツールを作成しました。以下を含む:

  • Perl
  • Python
  • Java
  • C/C++
  • PHP
  • Ruby
  • Go

サービスプロバイダー: 主要なメールサービスプロバイダーはSPFを広く採用し、実世界の経験に基づいて仕様の改良を支援しました:

  • Google (Gmail)
  • Microsoft (Outlook.com, Office 365)
  • Yahoo
  • AOL
  • その他の主要メールプロバイダー

DNSプロバイダー: DNSサービスプロバイダーはSPF管理ツールを開発し、SPF採用を促進するためのドキュメントを作成しました。

G.5 IETF Working Groups (IETFワーキンググループ)​

いくつかのIETFワーキンググループがSPFの開発に貢献しました:

MARID (MTA Authorization Records in DNS)ワーキンググループ:

  • DNS経由のメール認証に焦点を当てた
  • 送信者検証のさまざまなアプローチについて議論
  • SPFの初期開発のためのフォーラムを提供

ASRG (Anti-Spam Research Group):

  • アンチスパム技術の文脈でSPFを評価
  • セキュリティとプライバシーの考慮事項についてフィードバックを提供

DKIMワーキンググループ:

  • 補完的なメール認証技術に取り組んだ
  • SPFとDKIMが適切に連携することを保証

DMARCワーキンググループ:

  • SPFとDKIMを統合するDMARCを開発
  • メール認証ポリシーでSPF結果がどのように使用されるかを定義するのを支援

G.6 Tool and Library Developers (ツールおよびライブラリ開発者)​

以下のオープンソースプロジェクトと開発者は、SPFエコシステムに大きく貢献しました:

SPFライブラリ:

  • libspf2 (Cライブラリ) libspf2プロジェクトグループによる
  • Mail::SPF (Perlモジュール) Julian Mehnleおよび他の人々による
  • pyspf (Pythonライブラリ) Stuart GathmanとTerence Wayによる
  • spf4j (Javaライブラリ) さまざまな貢献者による

SPF検証ツール:

  • さまざまなプロバイダーによるオンラインSPFチェッカー
  • コマンドラインSPFテスター
  • DNS分析ツール

メールサーバー実装:

  • Postfix SPF統合
  • Exim SPFサポート
  • Sendmail SPF統合
  • Microsoft Exchange SPF実装

G.7 Documentation Contributors (ドキュメント貢献者)​

多くの人々がSPFドキュメント、チュートリアル、ベストプラクティスに貢献しました:

  • OpenSPF.org: コミュニティが維持するSPFドキュメントとリソース
  • IETFメーリングリスト: 詳細な技術的議論と明確化
  • ブログ著者および技術ライター: より広い聴衆にSPFを説明した人々
  • DNSプロバイダーのドキュメント: 実践的なSPF実装ガイドを提供した人々

G.8 Testing and Deployment (テストと展開)​

大規模なテストと展開を支援した組織と個人:

早期採用者:

  • ネットワークでSPFをテストした大手メールサービスプロバイダー
  • SPF検証を実装した企業組織
  • スパムフィルターコンポーネントとしてSPFを追加したISP

相互運用性テスト:

  • 実装間テストを実行した開発者
  • SPFテストスイートをホストした組織
  • バグと相互運用性の問題を報告した貢献者

G.9 Special Thanks (特別な謝辞)​

特別な感謝:

  • Meng Weng Wong: 元のSPF概念と、標準を開発するためにコミュニティを結集するための彼の不断の努力に対して

  • Mark Lentczner: 元のRFC 4408仕様の共著者として

  • Scott Kitterman: RFC 4408からRFC 7208への更新プロセスを主導し、SPFコミュニティの継続的な積極的なメンテナンスに対して

  • Wayne Schlitt: 広範な技術的貢献と実装経験に対して

  • Julian Mehnle: 主要なSPFライブラリの開発と仕様の明確化に対して

インターネットコミュニティ: SPFを使用、実装、議論し、メール認証の改善に貢献したすべての人々に。

G.10 Ongoing Work (進行中の作業)​

SPFの開発は以下を通じて継続されています:

  • 仕様の明確化: エッジケースを明確にするための継続的な議論
  • ライブラリの更新: SPF実装の積極的なメンテナンス
  • ベストプラクティスの開発: SPF展開のための進化するガイド
  • 他の標準との統合: DKIM、DMARC、および他のメールセキュリティ技術との調整

SPFコミュニティは引き続き活発であり、インターネット上のメール認証とセキュリティの改善に取り組んでいます。


最終注記: この仕様とSPFプロトコルは、すべての人のためにメールセキュリティを改善するために時間、専門知識、リソースを提供した無数のボランティアの努力なしには不可能でした。