本文へスキップ
株式会社オープンナレッジ
株式会社オープンナレッジ Open Knowledge Inc.
メール配送信頼工学・なりすまし(BEC)根絶 RFC 7208 (SPF) / RFC 6376 & 8463 (DKIM Ed25519) / RFC 7489 (DMARC) 対象:経営者様・広報/総務ご担当者様・インフラ技術者様

なりすましメールを根絶する SPF (RFC 7208) / DKIM (RFC 6376) / DMARC (RFC 7489) / ARC 暗号検証パイプライン詳解

監修:加藤 明生(株式会社オープンナレッジ 創業者・代表取締役CTO / ネットワークスペシャリスト / 登録情報処理安全確保支援士(第007617号))

1. 基本と全体像

この記事の要点と4つの章のご案内

身近なたとえで見る仕組み:「封筒の差出人(SPF)」「便箋の割印(DKIM)」「両方を突き合わせる受付検査(DMARC)」に関するイメージ写真
身近なたとえで見る仕組み:「封筒の差出人(SPF)」「便箋の割印(DKIM)」「両方を突き合わせる受付検査(DMARC)」
現場で陥りやすい落とし穴と回避策:企業のメール認証設定で頻発する3大トラブルに関するイメージ写真
現場で陥りやすい落とし穴と回避策:企業のメール認証設定で頻発する3大トラブル
なりすましメールを根絶する SPF (RFC 7208) / DKIM (RFC 6376) / DMARC (RFC 7489) / ARC 暗号検証パイプライン詳解 の各章におけるテーマと要点一覧表
章構成 主なテーマ 要点と確認ポイント
1. 基本と全体像 身近なたとえで見る仕組み:「封筒の差出人(SPF)」「便箋の割印(DKIM)」「両方を突き合わせる受付検査(DMARC)」 「自社の社名やメールアドレスを騙った偽メールが顧客に届く」「取引先を装った振込先変更メール(BEC)が届く」被害を止めるには、SPF・DKIM・DMARC の3点セットが不可欠です。
2. 現場での進め方と対策 現場で陥りやすい落とし穴と回避策:企業のメール認証設定で頻発する3大トラブル SPFは「指定された郵便局(IP)から出たか」、DKIMは「途中で本文が1文字も書き換えられていないか(電子封蝋)」、DMARCは「画面に表示される差出人名と一致しているか(Identifier Alignment)」を証明します。
3. 関連する制度とルール なぜ電子メールは、何も対策しないと「差出人のメールアドレス」を簡単に偽装できてしまうのですか? 当社のメール基盤(mx1〜mx3)では、RFC 8463準拠のEd25519 DKIM署名とDMARC p=reject(偽メールの全体拒否)を標準実装し、なりすましを構造的に遮断しています。
4. 詳しい技術仕様としくみ 「SPFは設定してあるから、うちの会社のドメインは安全」ではないのですか? インターネットのメールの仕組み(SMTP)は1980年代に設計されたため、紙のハガキと同じように「封筒の裏に書く差出人名」と「便箋の中に書く差出人名」を、送り主が好きな文字で自由に書き込める構造になっていたからです。この弱点を補うために後から作られたのが、SPF・DKIM・DMARCという3つの本人確認の仕組みです。
第1章〜第4章では、実務チェックリスト・公的制度基準・詳しい技術仕様を確認できます。
この記事のポイント
  • 01 「自社の社名やメールアドレスを騙った偽メールが顧客に届く」「取引先を装った振込先変更メール(BEC)が届く」被害を止めるには、SPF・DKIM・DMARC の3点セットが不可欠です。
  • 02 SPFは「指定された郵便局(IP)から出たか」、DKIMは「途中で本文が1文字も書き換えられていないか(電子封蝋)」、DMARCは「画面に表示される差出人名と一致しているか(Identifier Alignment)」を証明します。
  • 03 当社のメール基盤(mx1〜mx3)では、RFC 8463準拠のEd25519 DKIM署名とDMARC p=reject(偽メールの全体拒否)を標準実装し、なりすましを構造的に遮断しています。
場面別の実務確認ポイント(ご自身の役割に合わせてお読みいただけます) 全訪問者対応・3段階の構成
01 経営者・経理・営業・広報ご担当の方

なぜ「請求書メールが取引先のGmailに届かなくなった」というトラブルが起きるのか、そして自社のブランドを騙る偽メールをどう止めるのかが3分でわかります。

02 情報システム・DNS管理・Web運用担当の方

クラウドサービスを増やしすぎて起きる「SPF 10回DNSルックアップ制限エラー(PermError)」や、いきなり p=reject にして正規メールが止まる罠の回避手順を確認できます。

03 メール基盤職人・プロトコル研究者の方

後半の「第3章 Monograph」にて、DKIM relaxed/relaxed 正規化アルゴリズム、RFC 8463 Ed25519署名、DMARCアライメント論理式、Rust検証器を詳解しています。

第1章:Overview & Visual Architecture

身近なたとえで見る仕組み:「封筒の差出人(SPF)」「便箋の割印(DKIM)」「両方を突き合わせる受付検査(DMARC)」

実は、私たちが毎日見ている電子メールには「2つの差出人欄」があります。ひとつは郵便局員(メールサーバー同士)だけがやり取りに使う「外封筒の差出人(Envelope-From / Return-Path)」、もうひとつは私たちがメールソフトを開いたときに画面に表示される「中の便箋に書かれた差出人(Header-From)」です。

悪質な詐欺師は、外封筒には自分のサーバー名を書いてSPF検査をパスしながら、中の便箋には「有名銀行や取引先の社長のアドレス」を書いて送りつけてきます。受取人は外封筒を見ることができず、中の便箋しか見えないため、本物だと信じ込んでしまいます。

そこで3つの仕組みが連携して働きます。01「SPF」は外封筒が公認の発送場所から出たかを確認し、02「DKIM」は便箋そのものに「誰にも真似できないデジタル実印(電子署名)」を押して途中の書き換えを防ぎます。そして最後の要である03「DMARC(ディーマーク)」が、「画面に表示される便箋の差出人ドメインと、SPF・DKIMで確認されたドメインが本当に一致しているか(アライメント検査)」を突き合わせ、一致しない偽メールを「受取拒否(reject)」として玄関でシャットアウトするのです。

ポイント 01 RFC 7208 の厳格な上限
DNS検索 最大 10 回

SPFレコードの中で外部サービス(include:)を呼び出す回数が合計10回を超えると、即座に「PermError(恒久エラー)」となり認証が失敗します。

ポイント 02 次世代DKIM署名(RFC 8463)
Ed25519 + RSA 二重

従来の長いRSA-2048鍵に加え、短く強固な楕円曲線暗号Ed25519(公開鍵わずか32バイト)を併用することで、DNS応答の肥大化と改ざんを防ぎます。

ポイント 03 SCS評価制度・Google送信者要件
3点認証が必須基準

Gmail等の大手送信者ガイドラインおよび2026年SCS評価制度において、SPF・DKIM・DMARCによるドメイン保護は企業の必須インフラ要件です。

図解:SPF・DKIM・DMARCIdentifier Alignment(識別子アライメント)の3段階検証パイプライン
01 第1段階:経路と署名の暗号検証 SPF 検証 (RFC 7208) 対象:外封筒 (RFC5321.MailFrom) 送信元サーバーのIPアドレスが DNS TXTの許可リスト内か照合(上限10回) DKIM 検証 (RFC 6376 / RFC 8463) 対象:署名ドメイン (d=) と本文ハッシュ (bh=) relaxed/relaxed 正規化後のヘッダと本文を Ed25519 / RSA 公開鍵で電子署名検証 02 第2段階:DMARC アライメント検査 画面表示の差出人 (RFC5322.From) 利用者が目にする「From: 社長 <...@ok.st>」の ドメインが、SPF通過ドメインまたは DKIMの d= ドメインと一致しているか検査 SPF_Aligned OR DKIM_Aligned == true 転送・メーリングリスト保護 (ARC) 中継サーバーが元の認証結果に連鎖署名を付与し 正当な転送メールが誤遮断されるのを防ぐ 03 最終判定と証跡 一致時:受信箱へ配送 BIMI (認証ロゴ表示) により 公式メールであることを 受信画面上で視覚証明 不一致:p=reject 拒絶 偽装メールを玄関で破棄し 攻撃元IPの統計を rua= XMLレポート通知
認証技術(RFC番号)何を検査するか(役割)単独での限界と組み合わせの必然性
SPF (RFC 7208)外封筒(Return-Path)のドメインと送信元IPアドレスの照合画面に表示される差出人(Header-From)の偽装を見抜けない/メール転送で壊れる
DKIM (RFC 6376 / 8463)ヘッダと本文に対する公開鍵暗号署名(途中改ざん検知)転送には強いが、攻撃者が別ドメインで正当署名した偽メールを弾くにはDMARCが必要
DMARC (RFC 7489)画面上の差出人(Header-From)とSPF/DKIMドメインの一致確認p=quarantine または p=reject まで引き上げて初めて偽メールを自動遮断できる
ARC (RFC 8617)メーリングリストや転送中継時の「元の認証結果」の引き継ぎ署名学会・業界団体・社内転送環境で正規メールが不達になるのを防ぐ
第2章:Practical Field Guide & Self-Check

現場で陥りやすい落とし穴と回避策:企業のメール認証設定で頻発する3大トラブル

落とし穴 01:会計ソフト・メルマガ・顧客管理SaaSを次々と include: で追加し、「SPF 10回検索制限」を超えて全メールが不達になる

回避策:部門・用途ごとのサブドメイン分離(例:billing.example.jp)と不要な旧サービス記述の棚卸し

RFC 7208 Section 4.6.4 には、DNS増幅攻撃を防ぐため「SPFの検証中に発生するDNS検索(include, a, mx, redirect)は合計10回まで」という厳格な上限があります。1つの外部クラウドサービスを include: すると、そのサービスの中でさらに3〜4回の検索が連鎖することが多く、3〜4社のサービスを繋いだだけであっという間に10回を超えて PermError(全員への送信失敗)になります。これを防ぐ最も確実な方法は、社内連絡用・請求書システム用・広報配信用でサブドメインを分けるか、自動フラット化監視を導入することです。

落とし穴 02:DMARCを p=none(監視のみ)で登録しただけで安心し、何年も放置して偽メールが素通りし続ける

回避策:集計レポート(rua=)で社内の正規送信元をすべて把握した上で、p=quarantine → p=reject へ計画的に引き上げる

「DMARCを設定しました」という企業のうち、半数以上が v=DMARC1; p=none; のまま止まっています。p=none は「もし偽メールが見つかっても、何もしないでそのまま相手の受信箱へ届けてください」という意味であり、それ自体には防御力がありません。まずは rua=mailto:... で毎日のXMLレポートを受け取り、社内の複合機やWebフォームなどすべての正規メールにDKIM署名が付いていることを確認したら、速やかに p=quarantine(隔離)、そして最終目標である p=reject(拒否)へ移行することが大切です。

落とし穴 03:DKIMで c=simple/simple(非許容正規化モード)を使ってしまい、中継サーバーが空白や改行を1文字整えただけで署名エラーになる

回避策:空白や大文字小文字の揺れを吸収する c=relaxed/relaxed 正規化と、必須ヘッダ(From, Date, Subject, Message-ID)の署名固定

インターネット上のメール中継サーバー(MTA)は、配送の途中でヘッダの大文字・小文字を変えたり、行末の余分なスペースを削除したりすることがあります。DKIMの設定で simple モードを選んでいると、この無害な整形だけで「本文が改ざんされた!」と誤判定されてメールが届かなくなります。実運用では必ず c=relaxed/relaxed を指定し、意味を変えない空白の揺れを正規化してからハッシュ値を計算する設計にします。

今日から確認できる!なりすましメール・ビジネスメール詐欺(BEC)対策 3項目チェック

達成度: 0 / 3

クリックしてチェックを入れると、自社のメール信頼性と詐欺対策レベルに応じたアドバイスが表示されます。

上の3項目をクリックして、現在のメール設定と経理確認ルールを点検してみてください。

SPF / DKIM / DMARC / BIMI DNSレコード検証コマンドリファレンス

外封筒(RFC5321.MailFrom)と便箋(RFC5322.From)のドメイン一致(Identifier Alignment)およびDNSルックアップ上限(10回以下)をターミナルから直接検証する手順です。

プロトコル DNS検証コマンド 合格基準(RFC準拠条件)
SPF (RFC 7208) dig +short TXT ok.st | grep 'v=spf1' DNS参照回数が合計10回以内であり、末尾が -all(HardFail)で閉じられていること
DKIM (RFC 6376 / 8463) dig +short TXT selector._domainkey.ok.st k=ed25519 または 2048bit以上のRSA公開鍵が登録され、d= ドメインがFromと整合すること
DMARC (RFC 7489) dig +short TXT _dmarc.ok.st v=DMARC1; p=reject; adkim=s; aspf=s; により未承認メールを検疫・拒絶し、RUA集計先が有効なこと
関連コラム・豆知識

SPFレコードの末尾にある「~all(チルダ・オール)」と「-all(ハイフン・オール)」の1文字の違いとは?

会社のDNS設定(TXTレコード)を見ると、SPFの最後によく ~all(SoftFail:怪しいけれど一応通す)または -all(Fail:リスト以外のサーバーから来たら偽物と断定する)と書かれています。決して使ってはいけないのが +all(世界中の誰が送っても本物として許可する)や ?all(何もしない)です。これらを設定してしまうと、世界中のスパム業者に「うちの会社の名前を使って自由にメールを送っていいですよ」と宣言したのと同じになってしまいます。DKIMとDMARCを揃えた上で、~all または -all でしっかりと鍵を閉めることが大切です。

技術仕様・実装解説:Technical Architecture & Implementation — RFC 7208 / RFC 6376 / RFC 8463 / RFC 7489 Cryptographic Alignment & Canonicalization

DKIM relaxed/relaxed 正規化(RFC 6376)・Ed25519 署名(RFC 8463)と DMARC 識別子アライメント検証器の Rust 2024 実装

RFC 7208 / RFC 6376 / RFC 8463 / RFC 7489
SPF Lookup Limit Max 10 DNS Queries (RFC 7208)
DKIM Signature Alg ed25519-sha256 (RFC 8463)
DMARC Alignment adkim=s / aspf=s (Strict)
Transit Chain ARC-Seal (RFC 8617 cv=pass)
1. 設計仕様・数理モデル(Formal Predicate of DMARC Identifier Alignment & DKIM Relaxed Canonicalization)

RFC 7489 Section 3.1 における DMARC(Domain-based Message Authentication, Reporting, and Conformance)の通過条件は、単なる SPF または DKIM の単独成功ではなく、RFC 5322 From: ヘッダから抽出されたドメイン D_From と、検証を通過した認証識別子(SPF の RFC5321.MailFrom / HELO ドメイン D_SPF、または DKIM DKIM-Signature の d= タグドメイン D_DKIM)との間の識別子アライメント述語(Identifier Alignment Predicate)として定式化されます:

DMARC_Pass(M) ⇔ (SPF_Pass(M) ∧ Alignaspf(DFrom, DSPF)) ∨ (∃ σ ∈ ΣDKIM(M) : DKIM_Verify(σ) ∧ Alignadkim(DFrom, dσ))
Alignstrict(D1, D2) ⇔ Lower(D1) == Lower(D2),   Alignrelaxed(D1, D2) ⇔ OrgDomain(D1) == OrgDomain(D2)

また RFC 6376 Section 3.4.2 および Section 3.4.4 で定義される DKIM c=relaxed/relaxed 正規化関数 C_hdr, C_body は、MTA 中継時の軽微な空白改変に対するべき等性(C(C(x)) = C(x))を保証します。ヘッダ正規化 C_hdr は、(1) フィールド名をすべて ASCII 小文字へ変換、(2) 折り返し改行(CRLF + WSP)を単一スペース(0x20)へ展開、(3) 1つ以上の連続する空白文字(SP / HTAB)を単一スペースへ圧縮、(4) コロン前後の空白と行末空白を除去します。続いて RFC 8463(a=ed25519-sha256)では、正規化された本文の SHA-256 ダイジェストを bh= タグに格納し、選択されたヘッダ群と末端の dkim-signature ヘッダ(b= 値を空にしたもの)の SHA-256 ダイジェスト H_hdr に対して PureEdDSA(Ed25519)署名を検証します。

2. パラメータ・構成仕様(SPF / DKIM / DMARC / ARC DNS Record & Header Tag Specification) RFC 7208 / 6376 / 8463 / 7489 / 8617
Protocol / RFCDNS / Header TagProduction Value (ok.st)Cryptographic / Protocol Guarantee
SPF (RFC 7208)v=spf1 ip4:... -all≤ 3 Lookups, HardFail (-all)Eliminates PermError & unauthorized MTA spoofing
DKIM Key (RFC 8463)v=DKIM1; k=ed25519; p=...32-Byte Ed25519 Public Key128-bit security level, fits in single UDP DNS packet
DKIM-Signaturec=relaxed/relaxed; h=from:to:subject:date:message-id:fromOversigned From HeaderPrevents duplicate From header injection attacks
DMARC (RFC 7489)v=DMARC1; p=reject; sp=reject; adkim=s; aspf=sStrict Alignment + RejectDrops all unaligned Header-From spoofing attempts
ARC (RFC 8617)ARC-Authentication-Results / ARC-Message-Signature / ARC-SealInstance i=1..50 ChainPreserves pre-forwarding authentication across ML hops
3. 実装コード・設定仕様(src/mail/dmarc_dkim_canonical_verifier.rs)
Rust Edition 2024 (#![no_std] DKIM Relaxed Canonicalizer & DMARC Evaluator)
#![no_std]
#![forbid(unsafe_code)]

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AlignmentMode {
    Relaxed,
    Strict,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DmarcPolicy {
    None,
    Quarantine,
    Reject,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum DmarcDisposition {
    AcceptAuthenticated { spf_aligned: bool, dkim_aligned: bool },
    RejectOrQuarantine(DmarcPolicy),
    SpfLookupLimitExceeded(u8),
}

/// Case-insensitive ASCII byte comparison for domain labels (RFC 4343).
#[inline]
pub const fn ascii_eq_ignore_case(a: &[u8], b: &[u8]) -> bool {
    if a.len() != b.len() {
        return false;
    }
    let mut i = 0;
    while i < a.len() {
        let ca = if a[i] >= b'A' && a[i] <= b'Z' { a[i] + 32 } else { a[i] };
        let cb = if b[i] >= b'A' && b[i] <= b'Z' { b[i] + 32 } else { b[i] };
        if ca != cb {
            return false;
        }
        i += 1;
    }
    true
}

/// Evaluates RFC 7489 Section 3.1 Identifier Alignment between `RFC5322.From` domain
/// and an authenticated SPF or DKIM (`d=`) domain.
pub const fn check_domain_alignment(
    header_from_domain: &[u8],
    auth_domain: &[u8],
    mode: AlignmentMode,
) -> bool {
    if ascii_eq_ignore_case(header_from_domain, auth_domain) {
        return true;
    }
    match mode {
        AlignmentMode::Strict => false,
        AlignmentMode::Relaxed => {
            // Relaxed alignment allows subdomain relationship if Organizational Domain matches
            if auth_domain.len() > header_from_domain.len() {
                let prefix_len = auth_domain.len() - header_from_domain.len();
                if auth_domain[prefix_len - 1] == b'.' {
                    let (_, tail) = auth_domain.split_at(prefix_len);
                    return ascii_eq_ignore_case(header_from_domain, tail);
                }
            }
            false
        }
    }
}

/// Evaluates full DMARC disposition given SPF lookup count (RFC 7208 max 10),
/// SPF verification result, DKIM verification result, and alignment modes.
pub const fn evaluate_dmarc_pipeline(
    header_from_domain: &[u8],
    spf_mailfrom_domain: &[u8],
    spf_passed: bool,
    spf_dns_lookups: u8,
    dkim_d_domain: &[u8],
    dkim_signature_valid: bool,
    aspf: AlignmentMode,
    adkim: AlignmentMode,
    policy: DmarcPolicy,
) -> DmarcDisposition {
    if spf_dns_lookups > 10 {
        return DmarcDisposition::SpfLookupLimitExceeded(spf_dns_lookups);
    }

    let spf_aligned = spf_passed
        && check_domain_alignment(header_from_domain, spf_mailfrom_domain, aspf);
    let dkim_aligned = dkim_signature_valid
        && check_domain_alignment(header_from_domain, dkim_d_domain, adkim);

    if spf_aligned || dkim_aligned {
        DmarcDisposition::AcceptAuthenticated {
            spf_aligned,
            dkim_aligned,
        }
    } else {
        DmarcDisposition::RejectOrQuarantine(policy)
    }
}

/// Implements RFC 6376 Section 3.4.2 DKIM `relaxed` header canonicalization into `out_buf`.
/// Converts header field name to lowercase, unfolds CRLF+WSP, and compresses WSP runs to single 0x20.
pub fn canonicalize_dkim_header_relaxed(raw_header: &[u8], out_buf: &mut [u8]) -> Option<usize> {
    let mut colon_pos: Option<usize> = None;
    for (i, &b) in raw_header.iter().enumerate() {
        if b == b':' {
            colon_pos = Some(i);
            break;
        }
    }
    let c_idx = colon_pos?;
    let mut w = 0usize;

    // 1. Lowercase field name, stripping any WSP before colon
    for &b in &raw_header[..c_idx] {
        if b == b' ' || b == b'\t' {
            continue;
        }
        if w >= out_buf.len() {
            return None;
        }
        out_buf[w] = if b >= b'A' && b <= b'Z' { b + 32 } else { b };
        w += 1;
    }
    if w >= out_buf.len() {
        return None;
    }
    out_buf[w] = b':';
    w += 1;

    // 2. Skip leading WSP after colon and collapse internal WSP sequences
    let mut in_wsp = false;
    let mut started = false;
    for &b in &raw_header[(c_idx + 1)..] {
        if b == b'\r' || b == b'\n' {
            continue;
        }
        if b == b' ' || b == b'\t' {
            if started {
                in_wsp = true;
            }
        } else {
            if in_wsp {
                if w >= out_buf.len() {
                    return None;
                }
                out_buf[w] = b' ';
                w += 1;
                in_wsp = false;
            }
            if w >= out_buf.len() {
                return None;
            }
            out_buf[w] = b;
            w += 1;
            started = true;
        }
    }
    Some(w)
}
ご質問・公的制度の活用に関するお問い合わせ窓口

掲載資料に関するご質問・公的制度活用のご案内

本ページの図解や解説内容に関するご質問、経済産業省「SCS評価制度」やIPA「SECURITY ACTION・サイバーセキュリティお助け隊サービス」等の公的制度の確認方法について、落ち着いた参考窓口としてご案内しております。売り込みや営業連絡は一切行いませんので、社内勉強会やご家庭での確認資料としてご自由にご活用ください。

図解・ビジュアルの拡大表示

図表内のテキストもそのまま選択・検索可能です(ベクターSVG描画) 枠外クリックまたは ESC キーで閉じます