プログラマブルSASEの可能性に着手してみた
Cloudflare Zero Trust の Gateway は、組織のすべての HTTP/HTTPS トラフィックを可視化・制御する強力なプロキシです。Shadow IT の可視化と承認のマークなどもでき、ビジュアルも素晴らしい。
しかし、Gateway のダッシュボードやアナリティクスだけでは読み取れないものもあります。
- 社員が個人的に利用している Web アプリケーションの全てが読み取れているのか?
- ブラウザにインストールされた拡張機能がどんな外部サービスと通信しているか?
- IT 部門が認知していない SaaS ツールが業務に使われていないか?
これらは一般に Shadow IT と呼ばれ、セキュリティリスクの盲点になり得ます。
これらをもっと広範囲で検出・可視化するための手法を研究したかった。
このプロジェクトの動機は大きく2つあります。
- プログラマブル SASE の可用性研究 — Cloudflare の SASE 基盤(Gateway, Access, WARP)がどこまでプログラマブルに拡張できるのか。GraphQL Analytics API, Workers AI, D1, Pages など Developer Platform との連携がどのように機能し、実運用に耐えるのかを検証したかった
- 実際のニーズ — Gateway ダッシュボードのアナリティクスだけでは読み取りきれない、Web アプリケーション・開発ツール・ブラウザ拡張などを自動検出し可視化する手法の研究
本記事では、Gateway の HTTP ログを入力として、ドメインの種別分類などを行う App Discovery Dashboard のアーキテクチャと、チューニングの過程を紹介します。
システム概要

技術スタック
| レイヤー | 技術 | 役割 |
|---|---|---|
| フロントエンド | Hono JSX (SSR) + Tailwind CSS | クライアント JS ゼロの軽量 UI |
| バックエンド | Cloudflare Pages + Workers | エッジ実行(世界330+都市) |
| データベース | Cloudflare D1 (APAC リージョン) | アプリ・ユーザー・履歴の永続化 |
| AI 推論 | Workers AI (Llama 3.1 8B Instruct) | ドメイン種別の自動分類 |
| 認証 | Cloudflare Access | JWT ベースの SSO 認証 |
| データソース | Gateway GraphQL Analytics API | HTTP ログの集約取得 |
すべてが Cloudflare のプラットフォーム上で完結しており、外部サービスへの依存はありません。
データフロー — 6ステップのパイプライン
Step 1: Gateway ログ取得
Gateway GraphQL Analytics API (gatewayL7RequestsAdaptiveGroups) から、httpHost × email × applicationNames でグループ化された HTTP ログを取得します。
query ShadowITScan($accountId: String!, $since: String!, $until: String!) {
viewer {
accounts(filter: { accountTag: $accountId }) {
gatewayL7RequestsAdaptiveGroups(
filter: {
datetime_geq: $since
datetime_leq: $until
action: "allow"
}
limit: 10000
orderBy: [count_DESC]
) {
dimensions {
httpHost
email
applicationNames
}
count
}
}
}
}
これにより、どのドメインに、誰が、何回アクセスしたかがルートドメイン単位で集約されます。Cloudflare の DPI (Deep Packet Inspection) が認識したアプリケーション名も同時に取得します。
Step 2: ドメイン集約・除外フィルタ
サブドメインをルートドメインに集約し(例: api.slack.com, files.slack.com → slack.com)、除外パターンに一致するドメインをスキップします。
除外パターンは2種類あります:
- 手動登録 — 管理者が設定画面から登録(例:
*.gstatic.com) - 自動検出 — スキャン時に CDN/広告/EC サイトと判定されたドメインが自動登録
Step 3: Gateway HTTP ログ詳細メタデータ取得
新規ドメインに対して、5本の追加 GraphQL クエリを実行し、多角的な HTTP ログメタデータを収集します。
| クエリ | ディメンション | 取得データ |
|---|---|---|
| Query 1 | httpHost × categories |
Cloudflare のドメインカテゴリ |
| Query 2 | httpHost × httpResponseContentType × httpMethod |
Content-Type と HTTP メソッドの分布 |
| Query 3 | httpHost × url |
アクセス URL パスの上位 |
| Query 4 | httpHost × referer |
リファラ(参照元)分析と自ドメイン率 |
| Query 5 | httpHost × httpStatusCode × applicationNames |
ステータスコード分布と DPI アプリ名 |
重要なポイント: 外部 Web サイトへのアクセスは一切不要です。分類に必要なコンテキストは、すべて Gateway が記録した HTTP ログから構築されます。
これらのデータは GatewayDomainMeta インターフェースに集約されます:
interface GatewayDomainMeta {
cfCategories: string[]; // Cloudflare ドメインカテゴリ
topContentTypes: string[]; // レスポンス Content-Type 上位
topUrlPaths: string[]; // アクセス URL パス上位
httpMethods: string[]; // HTTP メソッド分布
topReferers: string[]; // リファラ上位(他ドメインのみ)
refererSelfRatio: number; // リファラの自ドメイン率 (0.0-1.0)
topStatusCodes: string[]; // HTTP ステータスコード分布
hasCfAppName: boolean; // CF DPI アプリ名の有無
totalRequests: number; // 総リクエスト数
uniqueUsers: number; // ユニークユーザー数
}
Step 4: 多層分類エンジン
ここがこのシステムの核心です。3段階のパイプラインでドメインを分類します。
Step 5: Cloudflare DPI 同期
Cloudflare の DPI が認識済みのアプリ (applicationNames) を「承認済み」ステータスに自動設定します。
Step 6: 管理者レビュー
ダッシュボードで Shadow IT 候補を確認し、承認 / 未承認 / レビュー中 / 対象外のステータスを設定します。
3層 AI 分類パイプライン — 精度とパフォーマンスの両立
ドメインの分類は3段階で行われます。上流で確定できたドメインは下流に渡さず、AI 呼び出しを最小限に抑えつつ高精度を維持します。
┌─────────────────────────────────────────────────────┐
│ ドメイン入力 │
└────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────┐
│ Layer 1: 静的 SaaS DB (300+ エントリ) │
│ → ヒット → 即確定(AI 呼び出しゼロ) │
└────────────────────┬────────────────────────────────┘
▼ ミスの場合
┌─────────────────────────────────────────────────────┐
│ Layer 2: ルールベース事前分類 (Gateway メタデータ) │
│ → CDN/広告/EC → 即確定 + 自動除外 │
│ → その他確定 → AI スキップ │
└────────────────────┬────────────────────────────────┘
▼ 判定不能の場合のみ
┌─────────────────────────────────────────────────────┐
│ Layer 3: Workers AI (Llama 3.1 8B) │
│ Gateway ログ全コンテキスト付きで推論 │
└─────────────────────────────────────────────────────┘
Layer 1: 静的 SaaS データベース
300以上の既知 SaaS ドメインを静的データベースでマッチします。Slack, Google Workspace, Salesforce, GitHub, Notion など主要サービスはドメイン名の完全一致だけで確定し、AI 呼び出しは発生しません。
// 例: known-saas.ts のエントリ
{ domain: 'slack.com', displayName: 'Slack', category: 'communication', appType: 'saas' }
{ domain: 'github.com', displayName: 'GitHub', category: 'development', appType: 'developer_tool' }
Layer 2: ルールベース事前分類
静的 DB にヒットしなかったドメインに対して、Gateway メタデータからルールベースで事前分類します。以下の検出ロジックが優先順位付きで実行されます。
1. 埋め込みリソース検出(最優先)
リファラ分析は最も強力なシグナルです。
リファラの自ドメイン率 < 10% = 他サイトへの埋め込みリソース
例えば、go-mpulse.net のリファラが skyticket.jp のみだった場合:
- このドメインは
skyticket.jpに埋め込まれた Akamai mPulse(パフォーマンス計測 SDK) - ユーザーが直接アクセスするアプリではない →
cdn_infraとして自動除外
Content-Type(画像/JS/CSS)、URL パス(/pixel, /beacon, /collect)、CF カテゴリ(Advertising, Technology)を組み合わせて、CDN/インフラまたは広告/トラッカーに分類します。
2. API 接続検出
バックエンド API として使われているだけのドメインを検出します。
条件:
- Content-Type が application/json 主体(50%以上)
- HTML コンテンツなし
- CF DPI アプリ名なし
→ cdn_infra(API 接続)として自動除外
URL パスが /api/, /v1/, /graphql, /webhook 等の API パターンも補助シグナルとして使います。
3. EC サイト検出(URL パスパターン)
Cloudflare のドメインカテゴリに依存せず、URL パスパターンだけで EC サイトを検出します。
const ecPathPatterns = [
'/collections/', '/products/', '/cart/', '/checkout/', // Shopify
'/shop/', '/item/', '/catalog/', '/category/', // 一般 EC
'/api/collect', // Shopify analytics
'/add-to-cart', '/wishlist',
];
例: uinfootwear.jp のトラフィックに /collections/kids-shoes や /api/collect が含まれていれば、靴の EC サイトとして ecommerce に分類 → 自動除外。
4. CF カテゴリベース分類
Cloudflare が付与したドメインカテゴリから種別を推定します。
| CF カテゴリ | 分類結果 |
|---|---|
| Content Servers, CDN, Infrastructure | cdn_infra (自動除外) |
| Advertising, Tracking | ad_tracker (自動除外) |
| Shopping, Clothing, Fashion, Food, Travel, Sports... | ecommerce (自動除外) |
| Social Network, Social Media | social_media |
| Streaming, Video, Audio | streaming |
| News | news_media |
| Financial, Banking | finance |
| Government | government |
| Education | education |
| SaaS, Productivity, Business | saas |
| Technology (DPI 名あり or HTML 主体) | developer_tool |
Layer 3: Workers AI
Layer 1・2 で判定できなかったドメインのみ、Workers AI (Llama 3.1 8B Instruct) に渡します。
ポイント: ドメイン名だけでなく、Gateway ログから収集した全メタデータをプロンプトに含めます。
Domain: example.com
=== Cloudflare Gateway HTTP ログ詳細 ===
CF DPI アプリケーション認識: なし(未認識)
Cloudflare Gateway カテゴリ: Technology
レスポンス Content-Type 上位: text/html (500), application/json (200)
アクセス URL パス上位: / (300), /dashboard (150), /api/users (100)
HTTP メソッド分布: GET (600), POST (100)
HTTP ステータスコード分布: 200 (650), 302 (50)
総リクエスト数: 700
ユニークユーザー数: 15
AI は17種別・26カテゴリの中から最適な分類を返します。
{
"displayName": "Example App",
"description": "チーム向けプロジェクト管理ツール",
"category": "project_management",
"appType": "saas"
}
自動除外 (Auto-Exclude)
以下の appType に分類されたドメインは、アプリ一覧に追加せず自動的に除外パターン(*.domain)に登録されます。
| appType | 対象 | 理由 |
|---|---|---|
cdn_infra |
CDN、インフラ基盤、バックエンド API | ユーザー向けアプリではない |
ad_tracker |
広告配信、トラッキング、アナリティクス SDK | サードパーティ埋込みリソース |
ecommerce |
EC サイト、ショッピング、個人消費系 | 業務利用ではなく個人利用 |
一度除外パターンに登録されると、次回以降のスキャンでも自動スキップされるため、管理者のレビュー負荷を大幅に軽減します。
チューニングの実例 — uinfootwear.jp 問題
実際のチューニングプロセスを紹介します。
問題
靴の EC サイト uinfootwear.jp がスキャンのたびに検出され続ける。Gateway ログを見ると、明らかにショッピングサイトであり Shadow IT ではない。
URL: /api/collect
Referer: https://www.uinfootwear.jp/collections/kids-shoes
Method: POST
Status: 200
CF カテゴリ: Society & Lifestyle, Clothing
CF アプリ名: なし
原因調査
調査の結果、複数の原因が重なっていたことが判明しました。
原因1: ecommerce が early-return 対象外
preClassifyFromGatewayMeta が正しく ecommerce を返しても、enrichDomain 関数の early-return 条件に ecommerce が含まれていなかったため、AI に渡されて別の種別に再分類されていました。
// Before (バグ)
if (preClassify.appType === 'cdn_infra' || preClassify.appType === 'ad_tracker') {
return { ... }; // ecommerce はここを通過せず AI へ
}
// After (修正)
if (preClassify.appType === 'cdn_infra' || preClassify.appType === 'ad_tracker' || preClassify.appType === 'ecommerce') {
return { ... }; // ecommerce も即確定
}
原因2: GraphQL categories ディメンションの不確実性
Gateway GraphQL Analytics API の categories ディメンションが期待通りにデータを返さない可能性がありました。カテゴリが空の場合、"Clothing" や "Society & Lifestyle" によるルールベース分類が一切機能しません。
原因3: カテゴリに依存しない検出パスがなかった
カテゴリが取得できない場合のフォールバック検出が存在しませんでした。
対策
ecommerceを early-return + auto-exclude 対象に追加- URL パスベースの EC 検出を追加 — カテゴリに依存しない
const ecPathPatterns = [
'/collections/', '/products/', '/cart/', '/checkout/',
'/shop/', '/item/', '/catalog/', '/category/',
'/api/collect', // Shopify analytics
'/add-to-cart', '/wishlist',
];
uinfootwear.jp のトラフィックには /collections/kids-shoes と /api/collect が含まれるため、カテゴリの取得成否に関係なく ecommerce として検出 → 自動除外されるようになりました。
理解できたこと
- 単一のシグナルに依存しない多層検出が重要
- GraphQL API のディメンションは必ずしも全て機能するとは限らない — フォールバックを用意する
- ルールベース分類で「即確定」した結果が下流で上書きされないよう、early-return のガード条件を正しく設定する
プログラマブル SASE としての考察
Developer Platform との連携
このプロジェクトを通じて確認できた、Cloudflare のプラットフォーム間連携の実用性:
| 連携パターン | 実装 | 所感 |
|---|---|---|
| Gateway → GraphQL Analytics | HTTP ログの集約クエリ (6本) | gatewayL7RequestsAdaptiveGroups は柔軟だが、使用可能なディメンションの組み合わせに制約がある。ドキュメントの充実が望まれる |
| Workers AI (D1 連携) | ドメイン分類結果を D1 に永続化 | エッジで推論 → 即座に DB 書き込み。レイテンシが低く快適 |
| Pages + Workers | SSR 全体をエッジで実行 | Hono JSX による SSR は開発体験が良い。クライアント JS ゼロでも十分なインタラクティビティ |
| D1 (APAC) | アプリ・ユーザー・履歴の永続化 | SQLite ベースのため馴染みやすい。リージョン指定で東京近傍に配置可能 |
| Access (JWT) | ダッシュボードの認証 | ミドルウェアで JWT を検証するだけ。SSO 連携が容易 |
Gateway ログの活用可能性
Gateway が記録する HTTP ログは、セキュリティ判定以外にも多くの情報を含んでいます。
- Content-Type 分布 → ドメインの性質(静的配信 vs Web アプリ vs API)を高精度に推定
- URL パスパターン → EC サイト、SaaS、開発ツール等のフレームワーク固有のパスを検出
- リファラ分析 → 埋め込みリソース(SDK, CDN, トラッカー)を高確度で識別
- DPI アプリケーション名 → 既知サービスの確実な識別
これらを組み合わせることで、ドメイン名だけの推論より大幅に精度の高い分類が可能になります。
改善の余地
- GraphQL Analytics のディメンション検証 — 全ディメンションの動作確認と、非対応ディメンションのグレースフル処理
- Shadow IT リスクスコアリング — ユーザー数・リクエスト数・データ転送量を複合したリスクスコアの導入
- Cron トリガー — 定期スキャンの自動化(現在は手動トリガー)
- Gateway ポリシー連動 — 検出した Shadow IT に対する Gateway ルールの自動生成
プロジェクト構成
shadow-it/
├── src/
│ ├── index.tsx # Hono エントリポイント (ミドルウェアチェーン)
│ ├── types.ts # 型定義 (AppType 17種, Category 26種, GatewayDomainMeta)
│ ├── routes/
│ │ ├── pages.tsx # SSR ページ (Dashboard, Apps, Users, Settings, About)
│ │ └── api.ts # API エンドポイント (Scan, Sync, Export)
│ ├── components/
│ │ └── Layout.tsx # 共通レイアウト・UI コンポーネント
│ ├── lib/
│ │ ├── gateway.ts # Gateway GraphQL (メイン + メタデータ5クエリ)
│ │ ├── scanner.ts # スキャン + 自動除外オーケストレーション
│ │ ├── enrichment.ts # 3層分類 (静的DB → ルール → AI)
│ │ ├── known-saas.ts # 300+ 既知 SaaS 静的データベース
│ │ ├── cf-sync.ts # Cloudflare DPI 同期
│ │ └── db.ts # D1 データアクセス層
│ └── middleware/
│ └── csrf.ts # CSRF Protection
├── schema.sql # D1 スキーマ定義
├── wrangler.toml # Cloudflare Workers 設定
└── public/ # 静的アセット
まとめ
App Discovery Dashboard は、Cloudflare Gateway の HTTP ログだけを入力として、ウェブアプリケーションなどを自動検出・分類・管理するダッシュボードです。
- プログラマブル SASE — Gateway のログデータを GraphQL Analytics API 経由で取得し、Workers AI と D1 を使ってエッジ上で完結する分類パイプラインを構築
- 3層分類 — 静的 DB → ルールベース → AI の多層構造で、精度とパフォーマンスを両立
- 自動除外 — CDN/広告/EC サイトを自動的に除外し、管理者のレビュー負荷を軽減
- 外部依存ゼロ — Web メタデータ取得や外部 API 呼び出しは不要。Gateway ログだけで動作
Cloudflare の Developer Platform は、セキュリティ運用の自動化において大きなポテンシャルを持っています。Gateway ログの豊富なコンテキストと、エッジで動作する AI・DB・認証基盤を組み合わせることで、従来は手作業が必要だったウェブアプリケーションなどの検出と管理を、ほぼ自動化できることが確認できました。
今後はより深いポータル連携などに研究を進めたい。
