mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-03-21 06:11:27 -07:00
170 lines
5.7 KiB
Markdown
170 lines
5.7 KiB
Markdown
# Bot Management API
|
|
|
|
## Workers: BotManagement Interface
|
|
|
|
```typescript
|
|
interface BotManagement {
|
|
score: number; // 1-99 (Enterprise), 0 if not computed
|
|
verifiedBot: boolean; // Is verified bot
|
|
staticResource: boolean; // Serves static resource
|
|
ja3Hash: string; // JA3 fingerprint (Enterprise, HTTPS only)
|
|
ja4: string; // JA4 fingerprint (Enterprise, HTTPS only)
|
|
jsDetection?: {
|
|
passed: boolean; // Passed JS detection (if enabled)
|
|
};
|
|
detectionIds: number[]; // Heuristic detection IDs
|
|
corporateProxy?: boolean; // From corporate proxy (Enterprise)
|
|
}
|
|
|
|
// DEPRECATED: Use botManagement.score instead
|
|
// request.cf.clientTrustScore (legacy, duplicate of botManagement.score)
|
|
|
|
// Access via request.cf
|
|
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
|
|
|
export default {
|
|
async fetch(request: Request): Promise<Response> {
|
|
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
|
const botMgmt = cf?.botManagement;
|
|
|
|
if (!botMgmt) return fetch(request);
|
|
if (botMgmt.verifiedBot) return fetch(request); // Allow verified bots
|
|
if (botMgmt.score === 1) return new Response('Blocked', { status: 403 });
|
|
if (botMgmt.score < 30) return new Response('Challenge required', { status: 429 });
|
|
|
|
return fetch(request);
|
|
}
|
|
};
|
|
```
|
|
|
|
## WAF Fields Reference
|
|
|
|
```txt
|
|
# Score fields
|
|
cf.bot_management.score # 0-99 (0 = not computed)
|
|
cf.bot_management.verified_bot # boolean
|
|
cf.bot_management.static_resource # boolean
|
|
cf.bot_management.ja3_hash # string (Enterprise)
|
|
cf.bot_management.ja4 # string (Enterprise)
|
|
cf.bot_management.detection_ids # array
|
|
cf.bot_management.js_detection.passed # boolean
|
|
cf.bot_management.corporate_proxy # boolean (Enterprise)
|
|
cf.verified_bot_category # string
|
|
|
|
# Workers equivalent
|
|
request.cf.botManagement.score
|
|
request.cf.botManagement.verifiedBot
|
|
request.cf.botManagement.ja3Hash
|
|
request.cf.botManagement.ja4
|
|
request.cf.botManagement.jsDetection.passed
|
|
request.cf.verifiedBotCategory
|
|
```
|
|
|
|
## JA4 Signals (Enterprise)
|
|
|
|
```typescript
|
|
import type { IncomingRequestCfProperties } from '@cloudflare/workers-types';
|
|
|
|
interface JA4Signals {
|
|
// Ratios (0.0-1.0)
|
|
heuristic_ratio_1h?: number; // Fraction flagged by heuristics
|
|
browser_ratio_1h?: number; // Fraction from real browsers
|
|
cache_ratio_1h?: number; // Fraction hitting cache
|
|
h2h3_ratio_1h?: number; // Fraction using HTTP/2 or HTTP/3
|
|
// Ranks (relative position in distribution)
|
|
uas_rank_1h?: number; // User-Agent diversity rank
|
|
paths_rank_1h?: number; // Path diversity rank
|
|
reqs_rank_1h?: number; // Request volume rank
|
|
ips_rank_1h?: number; // IP diversity rank
|
|
// Quantiles (0.0-1.0, percentile in distribution)
|
|
reqs_quantile_1h?: number; // Request volume quantile
|
|
ips_quantile_1h?: number; // IP count quantile
|
|
}
|
|
|
|
export default {
|
|
async fetch(request: Request): Promise<Response> {
|
|
const cf = request.cf as IncomingRequestCfProperties | undefined;
|
|
const ja4Signals = cf?.ja4Signals as JA4Signals | undefined;
|
|
|
|
if (!ja4Signals) return fetch(request); // Not available for HTTP or Worker routing
|
|
|
|
// Check for anomalous behavior
|
|
// High heuristic_ratio or low browser_ratio = suspicious
|
|
const heuristicRatio = ja4Signals.heuristic_ratio_1h ?? 0;
|
|
const browserRatio = ja4Signals.browser_ratio_1h ?? 0;
|
|
|
|
if (heuristicRatio > 0.5 || browserRatio < 0.3) {
|
|
return new Response('Suspicious traffic', { status: 403 });
|
|
}
|
|
|
|
return fetch(request);
|
|
}
|
|
};
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
See [patterns.md](./patterns.md) for Workers examples: mobile app allowlisting, corporate proxy exemption, datacenter detection, conditional delay, and more.
|
|
|
|
## Bot Analytics
|
|
|
|
### Access Locations
|
|
- Dashboard: Security > Bots (old) or Security > Analytics > Bot analysis (new)
|
|
- GraphQL API for programmatic access
|
|
- Security Events & Security Analytics
|
|
- Logpush/Logpull
|
|
|
|
### Available Data
|
|
- **Enterprise BM**: Bot scores (1-99), bot score source, distribution
|
|
- **Pro/Business**: Bot groupings (automated, likely automated, likely human)
|
|
- Top attributes: IPs, paths, user agents, countries
|
|
- Detection sources: Heuristics, ML, AD, JSD
|
|
- Verified bot categories
|
|
|
|
### Time Ranges
|
|
- **Enterprise BM**: Up to 1 week at a time, 30 days history
|
|
- **Pro/Business**: Up to 72 hours at a time, 30 days history
|
|
- Real-time in most cases, adaptive sampling (1-10% depending on volume)
|
|
|
|
## Logpush Fields
|
|
|
|
```txt
|
|
BotScore # 1-99 or 0 if not computed
|
|
BotScoreSrc # Detection engine (ML, Heuristics, etc.)
|
|
BotTags # Classification tags
|
|
BotDetectionIDs # Heuristic detection IDs
|
|
```
|
|
|
|
**BotScoreSrc values:**
|
|
- `"Heuristics"` - Known fingerprint
|
|
- `"Machine Learning"` - ML model
|
|
- `"Anomaly Detection"` - Baseline anomaly
|
|
- `"JS Detection"` - JavaScript check
|
|
- `"Cloudflare Service"` - Zero Trust
|
|
- `"Not Computed"` - Score = 0
|
|
|
|
Access via Logpush (stream to cloud storage/SIEM), Logpull (API to fetch logs), or GraphQL API (query analytics data).
|
|
|
|
## Testing with Miniflare
|
|
|
|
Miniflare provides mock botManagement data for local development:
|
|
|
|
**Default values:**
|
|
- `score: 99` (human)
|
|
- `verifiedBot: false`
|
|
- `corporateProxy: false`
|
|
- `ja3Hash: "25b4882c2bcb50cd6b469ff28c596742"`
|
|
- `staticResource: false`
|
|
- `detectionIds: []`
|
|
|
|
**Override in tests:**
|
|
```typescript
|
|
import { getPlatformProxy } from 'wrangler';
|
|
|
|
const { cf, dispose } = await getPlatformProxy();
|
|
// cf.botManagement is frozen mock object
|
|
expect(cf.botManagement.score).toBe(99);
|
|
```
|
|
|
|
For custom test data, mock request.cf in your test setup.
|