メール到達性を支える、プレスリリース内URLのドメイン評価の仕組み

  • URLをコピーしました!

こんにちは。バックエンドエンジニアの筒井(@tsuttsun_wind)です。

PR TIMESでは、プレスリリースを個人・メディアユーザーやメディアリストに向けてメール配信しています。

2026年1月中旬ごろ、Microsoft系のメールサービスを利用している一部ユーザーに対して、送信したメールが届かない問題が発生しました。

この事象により、多くのお客さまにご心配とご迷惑をおかけしたことを改めてお詫び申し上げます。

目次

背景

同時期には、Microsoft系メールサービス宛てにメールが届かない問題が広く報告されており、PR TIMESでも実際にメールが送られない問題が発生していました。

調査の過程で、受信側のメールサービスにおけるURLの評価やスパム判定が、メール到達性に影響し得ることが分かりました。 特に、メール本文に含まれるURLやリンク先ドメインが、受信側で利用される評価情報やブロックリスト判定の材料になる可能性があります。

theregister
Users fume at Outlook.com email 'carnage' Updated: Email flow slowed or stopped by mysterious forces at Microsoft
The Spamhaus Project
FAQs | Key definitions, including the definition of spam Find definitions relating to general terms Spamhaus uses, such as "What is a blocklist?" or "What is the definition of spam?"

これを受けて、エディターで入力されたタイトル・本文からURLを抽出し、ブロックリストへの掲載状況を事前に確認する仕組みを実装しました。

この記事では、その実装に至るまでの検討と、最終的な実装方法について紹介します。

メールが送付されない問題に関する弊社からのお知らせについては以下をご覧ください。

https://prtimes.jp/common/file/20260209_MAILincident_detail.pdf

実装方針

結論として、今回の実装では、社内で独自に推測するのではなく、後述する Spamhaus Domain Block List(DBL)という外部で運用されている評価情報の参照を行うことにしました。

当初は、次のような条件を「リスクのあるドメインの特徴」として利用できないか検討していました。

  • WHOIS登録者情報が匿名であること
  • ドメイン登録日が直近であること

Spamhausでは、以下のように未知の評価を持つドメインはリスクが高く扱われやすい、という趣旨の説明があります。

Reputations are built over time, and building a good reputation takes longer than building a bad reputation. Experience has shown that an unknown reputation has a much higher risk of emitting spam than known-good domains, so unknown reputations begin as “poor” by default.

(評判というものは長い時間をかけて築かれるものであり、悪い評判を得るよりも良い評判を得る方が時間がかかります。 経験上、評判が未知のドメインは、信頼性が確認されているドメインに比べてスパムを送信するリスクがはるかに高いため、未知のドメインの評価はデフォルトで「低(poor)」から始まるようになっています。)

The Spamhaus Project
FAQs | Domain Blocklists (DBL)| IP DNSBL | Spamhaus Find a definition and frequently asked questions relating to listings and usage of Spamhaus' Domain Blocklist (DBL).

そのため、上記の条件を 「リスクのあるドメインの特徴」 として利用できるのではないかと考えていました。

WHOIS情報を利用した判定

まずは、WHOIS情報を利用した判定を考えました。

その中でも、フォーマットが統一されていて扱いやすく、whois コマンドよりも rate limit の制限が緩いことから、初めにRDAPの利用を検討しました。
RDAPの詳細については以下をご確認ください。

https://www.nic.ad.jp/ja/newsletter/No64/0660.html

RDAPには次のような利点がありました。

  • whois コマンドと同等の情報が取得できる
  • フォーマットがJSON形式で統一されており、データを扱いやすい

一方で、一部レジストラが未対応であり、判定できないドメインが出てしまうという問題がありました。
実際の運用を考えると、判定できないケースが残ることは問い合わせにつながる可能性があり、採用を見送りました。

そこで次に、rate limit を超えない範囲で whois コマンドを使い、登録日や登録者情報が匿名化されているかを抽出できないかと考え、実際に試してみました。

たとえば、WHOIS情報が公開されているドメインでは以下のような情報が取得できます。

$ whois prtimes.jp

Domain Information:
[ドメイン情報]
[Domain Name]                PRTIMES.JP
[登録者名]                    PR TIMES Corporation.
[Registrant]                 PR TIMES Corporation.
...
[最終更新]                     2026/03/01 01:05:03 (JST)

一方で、レジストラ側の設定でドメインの登録者情報をマスクするオプションが有効化されている場合は、次のように出力されることがあります。

$ whois blocked.example.jp

Creation Date: 1996-07-21T02:00:00Z
...
Registrant Organization: BLOCKED EXAMPLE
Registry Registrant ID: REDACTED FOR PRIVACY
Registrant Name: REDACTED FOR PRIVACY

しかし、whois コマンドを利用した判定には次のような問題がありました。

  • 利用しているサービスによって表示形式が異なり、抽出ロジックが複雑になる
  • WHOIS情報の匿名化設定を行って登録者情報をマスクしているサービスは多く、リスクが低いドメインのサービスも多く含まれていた

つまり、「WHOIS情報が匿名化されている」「登録日が新しい」といった特徴だけでは、リスクのあるドメインを安定して絞り込むことが困難と結論づけました。

仮に実装を行えたとしても設計が複雑になるうえ、判定軸としてノイズが大きく誤検知も増えやすいため、「リスクのあるドメインの特徴」 を自前で推測する方針は断念しました。

Spamhaus DBLの参照

方針を変え、自前でドメインの特徴を推測するのではなく、外部で運用されているブロックリストを参照する方針に切り替えました。

その候補として、ドメインの評判や不正利用との関連性をもとに掲載情報を提供している Spamhaus を参照することにしました。
Spamhaus の概要や一般的な確認方法については、以下のPR TIMES MAGAZINEの記事でも紹介しています。

今回、Spamhaus が提供するドメイン単位のブロックリストである Spamhaus DBL を利用しました。
これを利用することで、対象ドメインが DBL に掲載されているかどうかを確認できます。

Spamhaus DBL は、ドメインのブロックリスト掲載状況を DNS のAレコード応答として返す仕組みになっています。

もし DBL にドメインが掲載されている場合は 127.x.x.x という形式で値が返ります。
この値は実際の接続先IPアドレスではなく、DBL上の掲載理由を表す応答コードとして扱います。

一方、掲載されていない場合は応答が空になるため、その結果をもとに掲載有無を判定します。

The Spamhaus Project
FAQs | Domain Blocklists (DBL)| IP DNSBL | Spamhaus Find a definition and frequently asked questions relating to listings and usage of Spamhaus' Domain Blocklist (DBL).

実装では、プレスリリースのタイトルや本文から抽出したドメインを Spamhaus が提供する照会用のDNS名に組み込みます。

たとえば example.com を判定する場合は、example.com をそのまま問い合わせるのではなく、Spamhaus の照会用 DNS 名に組み込んだ名前を作り、そのAレコードを問い合わせます。

なお、Spamhaus DBLへの掲載はドメインの危険性を断定するものではないため、PR TIMES側で配信の自動停止や配信自体の禁止をするのではなく、あくまでもユーザーに確認を促す用途に限定しています。

プレスリリースに含まれるURLからホストドメインを抽出する

Spamhaus DBLへ照会を行う前に、プレスリリースに含まれるURLから、ホストドメインだけを抽出する必要があります。

実装では、入力されたテキストから preg_match_all()http(s) URL を抽出します。 その後、parse_url()PHP_URL_HOST を指定して、判定対象となるホストドメインを取り出します。

なお、この正規表現はURLの仕様全体を網羅するためのものではなく、PR TIMESのプレスリリース編集画面で一般的に入力される http(s) のURLからホスト名を取り出す用途に限定しています。

そのため、厳密な検証は行わず、最終的なホスト抽出は parse_url() に委ねています。

class extractDomainService
{
    ...

    public static function extractDomainFromPressRelease(string $text): array
    {
        // テキスト中のhttp(s) URLを抽出
        if (!preg_match_all('/https?:\\/\\/[A-Za-z0-9._~-]+\\.[a-zA-Z]+/u', $text, $matches)) {
            return [];
        }

        $domainList = [];
        foreach ($matches[0] as $url) {
            $domain = strtolower(parse_url($url, PHP_URL_HOST));

            // falseや空文字が入ることはないが、PHPStanで警告されるため型を確定させる
            if (!is_string($domain) || $domain === '') {
                continue;
            }

            $domainList[] = $domain;
        }

        return array_values(array_unique($domainList));
    }
}

ドメイン判定の実装

前述の通り、Spamhaus DBL はドメインの掲載状況を DNS のAレコードとして返します。

実装では、dns_get_record() を使って Spamhaus DBL のAレコードを問い合わせます。
抽出したドメインを Spamhaus が提供する照会用DNS名に組み込み、問い合わせ種別として DNS_A を指定して照会します。

The Spamhaus Project
FAQs | Domain Blocklists (DBL)| IP DNSBL | Spamhaus Find a definition and frequently asked questions relating to listings and usage of Spamhaus' Domain Blocklist (DBL).
class DnsRecordResolver
{
    private const SPAMHAUS_DBL_QUERY = '<spamhaus-api-key>.dbl.dq.spamhaus.net';

    /**
     * @param string $domain 判定したいドメイン
     * @return array<int, array<string, mixed>>|false
     */
    public function resolve(string $domain)
    {
        $lookupDomain = sprintf(
            '%s.%s',
            $domain,
            self::SPAMHAUS_DBL_QUERY
        );

        return dns_get_record($lookupDomain, DNS_A);
    }
}

$resolver = new DnsRecordResolver();
$records = $resolver->resolve('blocked.example.jp');

if ($records === false) {
    // DNS問い合わせに失敗した場合
    return;
}

if (count($records) > 0) {
		// DBLに掲載されている場合
    return $records[0]['ip'];
} else {
		// DBLに掲載されていない場合
		return '';
}

レスポンス

ブロックリストに掲載されている場合は、配列に値が格納された状態で返却されます。

配列の中にあるIPを取り出し、DBL掲載ドメインとしてドメイン名と一緒にデータベースへ保存します。
配列が空の場合、今回の照会時点ではブロックリスト未掲載として扱い、未掲載ドメインとしてドメイン名をデータベースへ保存します。

また、ネットワークの問題などで dns_get_record()false を返した場合は照会失敗として扱います。この時、ユーザーの操作を妨げないことを優先し、その場では判定を行わない設計にしています。

この場合はDBL掲載・未掲載のどちらの結果としても保存せず、判定を bypass します。

// ブロックリストに掲載されていない場合
array(0) {
}

// ブロックリストに掲載されている場合
array(1) {
  [0]=>
  array(5) {
    ["host"]=>
    string(65) "blocked.example.jp.<spamhaus-api-key>.dbl.dq.spamhaus.net"
    ["class"]=>
    string(2) "IN"
    ["ttl"]=>
    int(15)
    ["type"]=>
    string(1) "A"
    ["ip"]=>
    string(9) "127.0.1.2"
  }
}

判定結果のキャッシュについて

Spamhaus への過剰なリクエストを防ぐため、DBL掲載・未掲載の判定結果は一定期間データベース上でキャッシュします。
キャッシュ期間が過ぎた場合にのみ再度 Spamhaus へ照会を行い、それ以外はデータベースに保存されている情報を参照します。

判定結果は時間の経過によって変わる可能性があります。
そのため、判定結果が変わった場合に問い合わせ対応や調査で確認できるよう、以前の結果との差分をログとして保存しています。

ドメインがブロック判定された時の挙動

プレスリリースの編集画面では、タイトルや本文にブロック対象ドメインが入力されると、画像のようなモーダルが表示されるようになります。

このモーダルは、配信設定へ進む直前や編集画面を開いたタイミングで表示されます。

この判定は、配信を機械的に止めるためではなく、事前に注意喚起するために利用しています。 ブロックリストの掲載状況は変化する可能性があり、掲載されていることが必ずしも悪意あるURLであることを意味しないためです。

また、配信を機械的に止めてしまうと企業ユーザーのプレスリリース配信に大きな影響が出てしまいます。
そのため、編集画面ではモーダルを表示し、ユーザーが事前に確認できる形にしました。

まとめ

今回の実装では、プレスリリースのタイトル・本文に含まれるURLからホストドメインを抽出し、Spamhaus DBL の掲載状況を参照して注意喚起できるようにしました。

当初は WHOIS / RDAP を利用した特徴ベースの判定も検討しましたが、網羅性や誤検知の観点から採用を見送りました。
最終的には、社内で独自にリスクを推測するのではなく、外部で運用されている評価情報を参照することで、判定根拠を説明しやすい構成にしています。

今後も配信品質を維持しながら、ドメイン判定を含む配信基盤全体の改善を進めていきます。

  • URLをコピーしました!

この記事を書いた人

2024年入社のバックエンドエンジニアです。

目次