0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Angularサイトを訪れるボットやAIクローラーを確認する

0
Posted at

AngularサイトにアクセスするクローラーがJavaScriptを実行しない場合、AngularのサービスやHTTP interceptorでは、その最初のリクエストを観測できません。今回はAngular SSRの前段にWebDecoyを置き、ページの表示を続けながらリクエストを確認します。

WebDecoy開発元による解説です。執筆・翻訳にはAIを使用しています。対象はWebDecoyのリクエスト検知SDKであり、FCaptchaではありません。

構成

ブラウザーやクローラー → Express → WebDecoy → Angular SSR

ミドルウェアは、公開カタログAPIとAngularのレンダリング処理の両方より前に置きます。ブラウザー側にAPIキーや拒否判定を持たせません。

静的ホストでAngularのファイルを配信し、APIだけ別のサーバーで動かしている場合、APIサーバーに追加するだけではページへのアクセスは見えません。配信元やCDNで観測する必要があります。CDNがNodeに転送せず返すキャッシュ応答も、この構成の対象外です。

実行用サンプル

今回の確認にはNode.js 26.5を使用しました。Angularは22.2、@webdecoy/expressと@webdecoy/nodeは0.18.0です。

git clone https://github.com/WebDecoy/node.git
cd node
git checkout 1a6b0700d45f4437311e359d3ab1ea91927fc0bf
cd examples/angular-monitor/app
npm ci --workspaces=false
npm run build
npm test
npm run serve

http://127.0.0.1:4300を開き、Load public catalogを押すと、公開デモデータを取得できます。

ng serveではなく、ビルド後のNodeサーバーを使います。確認したいのは、本番用サーバー内でのミドルウェアの配置だからです。サンプル全体には、コンポーネント、サーバー、lockfile、結合テストが含まれています。

Angularのハンドラーより前に配置する

src/server.tsでは、angularApp.handle(req)を呼ぶ前に以下を登録します。

import { webdecoy } from '@webdecoy/express';
import { tripwire, rateLimit } from '@webdecoy/node';

app.use(webdecoy({
  apiKey: process.env['WEBDECOY_API_KEY'] || undefined,
  mode: 'monitor',
  honeytoken: false,
  rules: [
    tripwire({ paths: ['/.env', '/wp-config.php'] }),
    rateLimit({ max: 60, window: 60 }),
  ],
}));

これは既存のExpressサーバーに組み込む部分です。server.ts全体をこのコードで置き換えるものではありません。既存の認証やルーティングは残します。

monitorでは、拒否という判定になっても、リクエストをアプリへ進めます。トリップワイヤーのパスには、本物の環境変数ファイルやWordPress設定ファイルを置いていません。

60秒で60リクエストという制限は検証用です。プロセス内のカウンターなので、複数レプリカ全体で共有される制限ではありません。実運用では適切な値や共有ストアを検討します。

honeytoken: falseはHTMLへの自動トラップ挿入を無効にします。このサンプルではhydration時のDOM変更を増やさず、まずサーバーリクエストの観測に絞っています。

実在する静的ファイルとhealthエンドポイントだけが検知を迂回します。Angularのサーバールートは次の設定です。

import { RenderMode, ServerRoute } from '@angular/ssr';

export const serverRoutes: ServerRoute[] = [
  { path: '**', renderMode: RenderMode.Server },
];

SSR、prerender、CSRの違いはAngularの公式ガイドで確認できます。どの配信経路をリクエストが通るかが、観測できる範囲を決めます。

判定とHTTP応答を分けて確認する

別のターミナルから実行します。

curl -i http://127.0.0.1:4300/api/products
curl -i http://127.0.0.1:4300/.env

カタログはHTTP 200と2件のデモ商品を返します。/.envへの要求では、ターミナルにconclusion: "DENY"、tripwire: true、wouldBlock: trueが記録されます。

それでもHTTP応答は404です。monitorモードがアプリ側の応答を維持するためです。ログにDENYがあることと、実際にブロックしたことは区別します。

サンプルは限定した判定情報だけを記録し、IP、全ヘッダー、リクエスト本文、認証情報はログに出力しません。

ダッシュボードへの送信を確認する

クラウドで確認する場合は、WebDecoyで作成した自分のキーをサーバー環境変数WEBDECOY_API_KEYに設定し、再起動します。Angularの設定、コンポーネント、interceptorにはキーを埋め込まないでください。

curl -i -A 'WebDecoy-Test/1.0' http://127.0.0.1:4300/api/products

Detections画面でTestラベルの記録を確認します。これは予約された検証用User-Agentであり、本物のAIクローラーによるアクセスではありません。

キーがない場合、サンプルはdashboardConfigured: falseとreportingError: trueを明示します。キーを設定しただけでも到達確認にはならないので、画面の記録と送信エラーを確認してください。

実行確認の結果

本番ビルドと以下の5件の結合テストが成功しました。

確認 結果
JavaScript実行前のHTMLにAngularの内容がある 成功
公開カタログAPI HTTP 200、商品2件
トリップワイヤー DENY判定、アプリの404を維持
予約されたテスト要求 ラベルとキー未設定の状態を確認
レート制限の観測 拒否判定を記録しつつカタログは200

APIキーなしで実行したため、クラウドへの送信や、本物のAIクローラーに対する検知精度を検証した結果ではありません。

デプロイ後に読むポイント

要求パス、分類、根拠となるシグナル、繰り返しの傾向を見ます。User-Agentのクローラー名は自己申告なので、検証済みの身元とは分けて扱います。

サンプルはloopbackにbindし、転送ヘッダーを信頼しません。プロキシの後ろで運用する場合は、その構成に合わせて信頼するプロキシを設定します。誤ると複数の訪問者を同じIPとして扱ってしまいます。

検知一覧は完全なアクセスログでもありません。ローカルで許可され送信されない要求や、キャッシュの影響があります。全リクエスト数の集計にはアクセスログを併用してください。

まずmonitorのまま観測すると、サイトを止めずに、どのアクセスに対応する必要があるかを確認できます。

英語版チュートリアル

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?