Dmarcのレポートの確認が面倒!〜自動でFailだけをチャットに通知

2026年2月26日

レポートの確認が面倒

メールのセキュリティを高めるため、Dmarc等の設定を行いました。レポートが毎日2通くらい来ます。

それくらいはいいのですが、圧縮された形式でxmlファイルがきます。解凍して、xmlのチェックめんどくさいです。

レポートの確認を簡単にするために

“dmarkのレポートが圧縮されたメールで毎日きます。その内容をダウンロードして解凍して、failなど気にすべき内容が届いたらその内容を要約して、Googleworkspaceのチャットに通知する方法を教えて下さい"

以下のステップでスクリプトを作成します。

  1. Gmailから抽出: 特定の条件(件名や送信元)でDMARCレポートのメールを検索。
  2. 解凍と解析: 添付ファイル(.zip または .gz)を解凍し、XMLの内容を読み取る。
  3. 判定: dkimspffail になっている件数をカウント。
  4. 通知: 問題がある場合のみ、Google Chat のスペースに要約を送信。

実際には以下の流れ

  1. Google Chat の設定 (Webhook URLの取得)
  2. Google Apps Script の作成
  3. 自動実行(トリガー)の設定

Webhookから

Webhook(ウェブフック)は、特定のイベント(フォーム送信、チャット投稿など)が発生した際に、Webサービスが自動的に別のサービスへリアルタイムにデータを送信(通知)する仕組みです。

Google Chat の設定 (Webhook URLの取得)

まず、通知先となるGoogle Chatの準備をします。

  1. 通知したいスペースを開きます。
  2. スペース名の横の ▽ をクリックし、「アプリと統合」「Webhookを追加」 を選択します。
  3. 名前(例: DMARC Bot)を入力して保存し、発行された Webhook URL をコピーしておきます。

アプリの統合から。

webhookを使いしました。

このWebhookのURLをGASのコードに含めます。

Gasのコードを書いてもらう

Dmarc Reportの宛先に来たメールを1週間分エラー通知してと頼みました。

/**
 * DMARCレポート自動解析・Google Chat通知スクリプト
 * 実行日の前日に届いたレポートを対象にします
 */
function processDmarcReports() {
  // --- 設定項目 ---
  const TARGET_EMAIL = "dmarc-reports@ngy.biz"; // 対象の受信アドレス
  const CHAT_WEBHOOK_URL = "ここにGoogle ChatのWebhook URLを貼り付け"; 
  // ----------------

  // 1. 実行日の前日の日付を取得 (YYYY/MM/DD形式)
  const yesterday = new Date();
  yesterday.setDate(yesterday.getDate() - 1);
  const dateString = Utilities.formatDate(yesterday, "JST", "yyyy/MM/dd");

  // 2. Gmailを検索 (宛先、日付、添付ファイルの有無)
  const query = `to:${TARGET_EMAIL} after:${dateString} has:attachment`;
  const threads = GmailApp.search(query);

  if (threads.length === 0) {
    console.log(`${dateString} 以降のDMARCレポートは見つかりませんでした。`);
    return;
  }

  threads.forEach(thread => {
    const messages = thread.getMessages();
    messages.forEach(message => {
      // メールの受信日時が「昨日」であることを厳密にチェック(念のため)
      const msgDate = message.getDate();
      if (msgDate < yesterday) return;

      const attachments = message.getAttachments();
      attachments.forEach(attachment => {
        let xmlContent = "";
        const fileName = attachment.getName().toLowerCase();

        try {
          // 3. 圧縮ファイルの解凍 (.zip または .gz)
          if (fileName.endsWith(".zip")) {
            const unzipped = Utilities.unzip(attachment);
            xmlContent = unzipped[0].getDataAsString();
          } else if (fileName.endsWith(".gz")) {
            xmlContent = Utilities.ungzip(attachment).getDataAsString();
          } else if (fileName.endsWith(".xml")) {
            xmlContent = attachment.getDataAsString();
          }

          if (xmlContent) {
            // 4. XMLの解析と通知
            analyzeAndNotify(xmlContent, CHAT_WEBHOOK_URL, message.getSubject());
          }
        } catch (e) {
          console.error(`ファイル名 ${fileName} の処理中にエラー: ${e.message}`);
        }
      });
    });
    // 二重通知防止のため、処理したスレッドに既読をつける、またはラベルを貼ることを推奨
    thread.markRead(); 
  });
}

/**
 * XMLを解析して fail があれば Google Chat に送信する
 */
function analyzeAndNotify(xmlString, webhookUrl, subject) {
  const document = XmlService.parse(xmlString);
  const root = document.getRootElement();
  
  // 名前空間を考慮せず要素を取得(DMARC XMLはシンプルなためgetChildrenで対応可能)
  const records = root.getChildren("record");
  
  let totalFail = 0;
  let failDetails = "";

  records.forEach(record => {
    const row = record.getChild("row");
    const policyEvaluated = row.getChild("policy_evaluated");
    const spf = policyEvaluated.getChild("spf").getText();
    const dkim = policyEvaluated.getChild("dkim").getText();
    const sourceIp = row.getChild("source_ip").getText();

    // SPF または DKIM が fail の場合をカウント
    if (spf === "fail" || dkim === "fail") {
      totalFail++;
      // 通知が長くなりすぎないよう、直近数件のみ詳細を表示
      if (totalFail <= 10) {
        failDetails += `\n・IP: ${sourceIp} (SPF: ${spf}, DKIM: ${dkim})`;
      }
    }
  });

  // 5. 不合格(fail)があった場合のみ Google Chat へ通知
  if (totalFail > 0) {
    const payload = {
      "text": `⚠️ *DMARC Alert*\n*Subject:* ${subject}\n*Result:* Fail が ${totalFail} 件検出されました。\n*Details:* ${failDetails}` + (totalFail > 10 ? "\n(他多数...)" : "")
    };

    const options = {
      "method": "post",
      "contentType": "application/json",
      "payload": JSON.stringify(payload)
    };

    UrlFetchApp.fetch(webhookUrl, options);
  }
}

通知された

無事、実行したら、通知されました!GoogleWorkspaceのチャットに。

Failがちょびちょび出ています!

要因分析もお願いする

どこでエラーが出ているかは、わかりましたが、要因分析もお願いしてみました。

実行時間を指定 〜トリガーの追加

通知はされましたが、毎回自分で実行はしないと思うので、時間が来たら自動的に通知するようにします。

土曜日の夜中に起動して、通知するようにトリガーを設定しました。

実行する関数を選択して、時間を指定していきます。

そしてトリガーが発動したら1週間分エラーレポートが、土曜日に通知されるはずです。

土曜日の朝を迎えての実行結果

朝起きたら、通知されていました!効率化!

過去のFailは出ていますが、最近は、エラー出てなさそう。やったね。また来週の土曜日にどんな通知が来るか楽しみです。これで毎日来るDmarcレポートをついチェックして、時間を無駄にしてしまうことは無さそうです。

感想

毎日来るレポートが圧縮ファイルでは耐えきれませんので、効率化できてよかったです。

こういった複数回のちょっと処理はどんどんAIがなんとかしてくれますね。まあAIというかGASですが。

そんなところで

PVアクセスランキング にほんブログ村