Cloudflare Turnstile の Analytics
はじめに
Turnstile の Analytics 機能を確認します。
現時点(2024年10月)でダッシュボードで確認できる項目は下記の通りです。
今後の機能拡張はあると思います
項目 | 概要 |
---|---|
Visitor Solve Rate 訪問者解決率 |
チャレンジ成功数 / チャレンジ発行数 |
API Solve Rate API 解決率 |
トークン検証成功数 / チャレンジ発行数 |
Widget Traffic ウィジェットトラフィック |
ウィジェットの解決数および未解決数 |
Top Actions トップアクション |
利用者がウィジェット用に作成したラベル(=アクション)ごとのチャレンジ数 |
また、これらの元データを直接クエリーできる GraphQL のデータセット turnstileAdaptiveGroups
が提供されています。例えば、ダッシュボードをインスペクトすると、裏で出ているクエリーを確認できます。
いくつかの例を使い、それぞれを並行で確認してみます。
例1
状況
- チャレンジ発行 = 2
- チャレンジ成功 = 2
- トークン検証成功 = 0
ダッシュボード
- 訪問者解決率 100% (2/2)
- API 解決率 0% (0/2)
GraphQL
データセットは turnstileAdaptiveGroups
Adaptive とあるように、内容はアダプティブサンプリングです。
リクエスト数が増えるにつれ、サンプルレートが低くなる
クエリ
query ($ACCOUNTTAG: string!, $DATETIMEG: Time, $DATETIMEL: Time, $SITEKEY: string!, $ACTION: string!){
viewer {
accounts(filter: {accountTag: $ACCOUNTTAG}) {
turnstileAdaptiveGroups ( filter: {
datetime_geq: $DATETIMEG
datetime_lt: $DATETIMEL
siteKey_like: $SITEKEY
action_like: $ACTION
}
limit: 10
orderBy: [count_DESC]
)
{
count
dimensions {
eventType
datetime
}
}
}
}
}
バリアブル
{
"ACCOUNTTAG": "アカウントID",
"DATETIMEG": "2024-10-09T14:00:00+09:00",
"DATETIMEL": "2024-10-09T14:10:00+09:00",
"SITEKEY" : "サイトキー",
"ACTION": "ウィジェットのラベル名"
}
時刻の書式は +09:00 で JST にしています。
結果
ダッシュボードと同様に
challenge_issued
(発行したチャレンジ)が 2回
challenge_non_interactive_solved
(非インタラクティブな解決)が 2回
発生しいます。
{
"data": {
"viewer": {
"accounts": [
{
"turnstileAdaptiveGroups": [
{
"count": 1,
"dimensions": {
"datetime": "2024-10-09T05:05:00Z",
"eventType": "challenge_issued"
}
},
{
"count": 1,
"dimensions": {
"datetime": "2024-10-09T05:09:29Z",
"eventType": "challenge_non_interactive_solved"
}
},
{
"count": 1,
"dimensions": {
"datetime": "2024-10-09T05:05:00Z",
"eventType": "challenge_non_interactive_solved"
}
},
{
"count": 1,
"dimensions": {
"datetime": "2024-10-09T05:09:29Z",
"eventType": "challenge_issued"
}
}
]
}
]
}
},
"errors": null
}
カウント属性からイベント発生時刻(datetime)を削除し、イベントタイプだけでまとめます。
クエリ
query ($ACCOUNTTAG: string!, $DATETIMEG: Time, $DATETIMEL: Time, $SITEKEY: string!, $ACTION: string!){
viewer {
accounts(filter: {accountTag: $ACCOUNTTAG}) {
turnstileAdaptiveGroups ( filter: {
datetime_geq: $DATETIMEG
datetime_lt: $DATETIMEL
siteKey_like: $SITEKEY
action_like: $ACTION
}
limit: 10
orderBy: [count_DESC]
)
{
count
dimensions {
eventType
#datetime
}
}
}
}
}
各イベントの発生回数が取れました。
{
"data": {
"viewer": {
"accounts": [
{
"turnstileAdaptiveGroups": [
{
"count": 2,
"dimensions": {
"eventType": "challenge_non_interactive_solved"
}
},
{
"count": 2,
"dimensions": {
"eventType": "challenge_issued"
}
}
]
}
]
}
},
"errors": null
}
例2
状況
- チャレンジ発行 = 3
- チャレンジ成功 = 3
- トークン検証成功 = 2
ダッシュボード
訪問者解決率 100%(3/3)
API 解決率 66.67%(2/3)
GraphQL
項目 | 計算 | 結果 |
---|---|---|
訪問者解決率 | (challenge_non_interactive_solved + challenge_interactive_solved ) / challenge_issued
|
100%(3/3) |
API 解決率 | (challenge_non_interactive_siteverify_solved + challenge_interactive_siteverify_solved ) / challenge_issued
|
66.67%(2/3) |
結果
{
"data": {
"viewer": {
"accounts": [
{
"turnstileAdaptiveGroups": [
{
"count": 3,
"dimensions": {
"eventType": "challenge_non_interactive_solved"
}
},
{
"count": 3,
"dimensions": {
"eventType": "challenge_issued"
}
},
{
"count": 2,
"dimensions": {
"eventType": "challenge_non_interactive_siteverify_solved"
}
}
]
}
]
}
},
"errors": null
}
例3
状況
- チャレンジ発行 = 13
- チャレンジ成功 = 11
- トークン検証成功 = 4
ダッシュボード
訪問者解決率 84.62%(11/13)
API 解決率 30.77%(4/13)
GraphQL
結果
{
"data": {
"viewer": {
"accounts": [
{
"turnstileAdaptiveGroups": [
{
"count": 13,
"dimensions": {
"eventType": "challenge_issued"
}
},
{
"count": 11,
"dimensions": {
"eventType": "challenge_non_interactive_solved"
}
},
{
"count": 4,
"dimensions": {
"eventType": "challenge_non_interactive_siteverify_solved"
}
},
{
"count": 1,
"dimensions": {
"eventType": "challenge_siteverify_failed_double_redemption"
}
}
]
}
]
}
},
"errors": null
}
未解決
の分析
未解決
が 2 あります。その属性を知りたいところです。
ただ、今のところ eventType
で未解決
を示すものはないようにみえます。(継続調査中)
発行されたチャレンジ全体から、どれが未解決のものなのか、あぶり出すことができていません。
それができれば未解決
チャレンジの IP アドレス、AS 番号、OS 、ブラウザー情報などを収集できます。
Dimesions すべて取ったら
"turnstileAdaptiveGroups": [
{
"count": 2,
"dimensions": {
"action": "ウィジェットの名前",
"asn": 13335,
"browserMajor": 129,
"browserName": "Chrome",
"countryCode": "JP",
"date": "2024-10-09",
"datetime": "2024-10-09T09:55:18Z",
"datetimeDay": "2024-10-09T00:00:00Z",
"datetimeFifteenMinutes": "2024-10-09T09:45:00Z",
"datetimeFiveMinutes": "2024-10-09T09:55:00Z",
"datetimeHalfOfHour": "2024-10-09T09:30:00Z",
"datetimeHour": "2024-10-09T09:00:00Z",
"datetimeMinute": "2024-10-09T09:55:00Z",
"eventType": "challenge_issued",
"hostname": "接続先ののホスト名",
"ipv4": "",
"ipv6": "アクセス元の IPv6 アドレス",
"osMajor": 0,
"osName": "Mac OS X",
"siteKey": "サイトキー",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36"
}
},
補足
Cloudflare で HTTP リクエストをプロキシーする設計に変更すれば、チャレンジ(WAF)に失敗したリクエストの情報を詳細に得ることができたり、より多層な保護をアプリケーションに実装することができます。
Discussion