1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
package analytics
import (
"cmp"
"slices"
"strings"
"github.com/x-way/crawlerdetect"
"src.eltr.ac/eltrac/audidi/data"
)
type AnalyticsResult struct {
Client string
// 0 = Human
// 1 = Human operating non-browser tools
// 2 = Automated crawler
// 3 = Malicious crawler
BotIndex float32
}
func Analyze(log data.LogEntry) *AnalyticsResult {
uaResult := analyzeUA(log.UserAgent)
ja4Client, ja4Result := analyzeJA4(log.JA4)
matchScore := analyzeClientMatch(log.UserAgent, ja4Client)
ASNScore := analyzeASN(log.ASN)
return &AnalyticsResult{
Client: cmp.Or(ja4Client, log.UserAgent),
BotIndex: (uaResult + ja4Result + matchScore + ASNScore) / 4,
}
}
func analyzeUA(ua string) float32 {
if crawlerdetect.IsCrawler(ua) {
return 2
} else {
return 0
}
}
func analyzeJA4(ja4 string) (client string, botIndex float32) {
client, clientType := LookupClientByJA4(ja4)
return client, float32(clientType)
}
func analyzeClientMatch(ua string, ja4client string) float32 {
uaClient := strings.ToLower(ua)
ja4Client := strings.ToLower(ja4client)
if strings.Contains(uaClient, ja4Client) {
return 0
} else {
return 3
}
}
func analyzeASN(asn int) float32 {
DataCenterASNs := []int{
16509, 14618, 8987, 38895, 8075, 8068, 12076, 15169, 396982, 19527, 31898, 36351,
37963, 45102, 24429, 132203, 45090, 55990, 136907, 38365,
13335, 20940, 16625, 21342, 63949, 54113, 60068, 212238, 15133, 19551,
14061, 20473, 16276, 24940, 51167, 12876, 60781, 28753, 9009,
53667, 49505, 9123, 197540, 8560, 6724, 210644, 55293,
26496, 46606, 32244, 26347, 33070, 19994, 22612, 19871, 2635,
}
if slices.Contains(DataCenterASNs, asn) {
return 2
}
return 0
}
|