PHPでPubSubHubbub通知クライアントを自作してみた – 技術と実装ポイントまとめ

# はじめに
こんにちは！高校生でプログラミング勉強中のCureSabaです。

今回は、**PHPでPubSubHubbub（WebSub）通知を受け取るクライアント（エンドポイント）を自作した技術的解説**をします。  
YouTubeの新着動画通知をはじめとする「プッシュ型イベント通知」をPHPで完全に受け取る方法・技術ポイント・セキュリティ・実装例まで、詳しく紹介します。

# PubSubHubbub（WebSub）とは？

PubSubHubbub（WebSub）は、特定のトピック（例：YouTubeチャンネルの新着動画）が更新された際、購読者（Subscriber）に即時通知を届けるプロトコルです。  
YouTubeやブログの「新着通知BOT」などで広く利用されています。

- **Publisher**（例：YouTube）が情報を発信  
- **Hub**（中継サーバ）が通知を管理  
- **Subscriber（ここではPHPサーバ）** が「callback.php」で通知を受け取る

# PHPでPubSubHubbubクライアントを作る技術ポイント

## 1. サブスクライブ（購読登録）を行う

購読登録は自前PHPスクリプトまたは[php-subscriberライブラリ](https://github.com/CureSaba/php-subscriber)で行います。  
例: YouTubeの動画フィードを購読する場合

```php
$hub_url = "https://pubsubhubbub.appspot.com/subscribe";
$callback_url = "https://example.com/callback.php?token=..."; // あなたの受信エンドポイント
$secret = bin2hex(random_bytes(32)); // HMAC署名用
$verify_token = bin2hex(random_bytes(16)); // Challenge検証用

$subscriber = new Pubsubhubbub\Subscriber\Subscriber(
    $hub_url, $callback_url, false, $secret, 'async', $verify_token, 691200
);
$subscriber->subscribe("https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCxxxxxxx");
```

これでYouTubeやHub側に「新着通知はこのエンドポイントに送ってくれ」と申請します。

## 2. PHPでPubSubHubbub通知を受信する（callback.php）

### GETリクエスト（チャレンジ検証）

Hubからの初回検証時は、GETで`hub.challenge`等が送られます。  
このチャレンジ値をそのまま返さないと、通知が有効化されません。

```php
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $mode = $_GET['hub_mode'] ?? '';
    $challenge = $_GET['hub_challenge'] ?? '';
    $topic = $_GET['hub_topic'] ?? '';
    $verify_token = $_GET['hub_verify_token'] ?? '';
    // DB等でverify_tokenやトピックURLが一致するか検証
    if ($mode === 'subscribe' && $challenge && $verify_token === $expected_token) {
        echo $challenge;
        exit;
    }
    http_response_code(400);
    exit('Invalid request');
}
```

### POSTリクエスト（実際の通知受信）

新着イベント時、HubからPOSTでXMLデータが届きます。  
**セキュリティのため、HMAC署名（X-Hub-Signatureヘッダ）を必ず検証！**

```php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $body = file_get_contents('php://input');
    $signature = $_SERVER['HTTP_X_HUB_SIGNATURE'] ?? '';
    list($algo, $hash) = explode('=', $signature, 2);
    $expected_hash = hash_hmac($algo, $body, $secret);
    if (!hash_equals($expected_hash, $hash)) {
        http_response_code(401);
        exit('Invalid signature');
    }
    // XMLパース
    $xml = simplexml_load_string($body);
    // 必要な情報抽出・通知処理...
    // 例: YouTubeの場合は動画IDやタイトルを取得してDiscord等に送信
    echo 'OK';
}
```

## 3. セキュリティ・運用の工夫

- **HMAC署名（X-Hub-Signature）検証**：HubからのPOSTは必ず署名がつくので、secretと照合して改ざん/なりすましを防止
- **verify_tokenの保存＆GET時検証**：サブスクライブ時に発行したものと一致するかDB等で管理
- **重複通知防止**：動画IDなどをDBで記録し、同じIDは1回だけ通知する仕組み
- **エラーや不正通知時の詳細ログ出力**：トラブル時の原因が追えるように推奨

## 4. 実際の構成例・全体像

- サブスクライブ申請：subscribe.php等で申請
- 通知受信：callback.php
- DBで状態管理（トークン・期限・前回通知ID）
- Discord Webhook等への自動通知

# まとめ・学び

今回PHPでPubSubHubbubクライアントを自作したことで、以下のような深い知見・技術的成長がありました。

- **プロトコル設計への理解が深まった**  
  普段はAPIを「使う側」ですが、PubSubHubbubでは「プロトコルの流れ」— サブスクライブ、チャレンジ応答、HMAC署名検証、期限管理といった一連のやり取りを自分で実装することで、Webのセキュアなイベント配信の本質を学べました。

- **セキュリティの現場感覚が身に付いた**  
  署名検証やverify_tokenによる本人確認、タイミング攻撃対策（hash_equalsの重要性）、なりすまし・リプレイ防止など、ただAPIを叩くだけでは身につかない本格的なセキュリティ設計力が鍛えられました。

- **イベント駆動&非同期処理、状態管理の難しさと対策**  
  PubSubHubbubの「購読期限」や「再サブスクライブ」「重複通知防止」など、サーバー側で状態を持ち、かつ非同期でイベントをハンドリングする難しさを体感しました。結果として、DB設計やエラーハンドリング、再送/失敗時のリカバリ設計なども考慮できるようになりました。

- **PHPでの実践的なWebフック設計・外部連携ノウハウ**  
  Discord Webhookへの通知や、テンプレート置換の柔軟性、JSONやXMLパースの現場的なテクニックを身につけました。  
  さらに、将来的には他のサービス（MastodonやMisskeyなど）への応用も可能な汎用的な受信エンドポイント設計の基礎を得ることができました。

- **ソース読解・公式仕様との付き合わせの重要性**  
  [YouTube公式ドキュメント](https://developers.google.com/youtube/v3/guides/push_notifications)や外部ライブラリのソースを実際に読み、仕様を自分で検証して「なぜこうなるのか？」を理解できるようになりました。

この経験は、単なるBOT開発にとどまらず、「Webサービスを自分で構築・連携する」ための礎となるものでした。

# 終わりに
この記事が役に立ったら、ぜひコメント・シェアをお願いします！  
質問や改善点なども気軽にどうぞ！

それでは、良いプログラミングライフを！

# 参考・ソース

- [YouTube Push Notifications (PubSubHubbub) - 公式ドキュメント](https://developers.google.com/youtube/v3/guides/push_notifications)
- [YouTube のチャンネルの更新通知を受け取って Lambda を実行する
](https://zenn.dev/meihei/articles/01cd06f729056a)
- [CureSaba/php-subscriber](https://github.com/CureSaba/php-subscriber)（自作PHPクライアントライブラリ）