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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
package waf
import (
"net/url"
"regexp"
"strings"
)
// WAFConfig is stored per Service and controls which checks are active.
type WAFConfig struct {
Enabled bool `json:"enabled"`
BlockSQLi bool `json:"blockSQLi"`
BlockXSS bool `json:"blockXSS"`
BlockPathTraversal bool `json:"blockPathTraversal"`
BlockCommandInj bool `json:"blockCommandInj"`
BlockLargeBody bool `json:"blockLargeBody"`
MaxBodyKB int `json:"maxBodyKB"` // 0 = use default (512)
}
// BlockReason is returned when a request is blocked.
type BlockReason struct {
Rule string
Matched string
}
var (
sqliRe = regexp.MustCompile(
`(?i)(` +
`\b(union\s+select|insert\s+into|drop\s+table|drop\s+database|alter\s+table|exec\s*\(|execute\s*\(|xp_cmdshell)\b` +
`|--\s|;\s*--` +
`|'\s*(or|and)\s+'?\d` +
`|\bor\b\s+\d+\s*=\s*\d+` +
`|\band\b\s+\d+\s*=\s*\d+` +
`|'\s*(or|and)\s+'[^']*'\s*=\s*'` +
`|\/\*.*?\*\/` +
`)`,
)
xssRe = regexp.MustCompile(
`(?i)(` +
`<\s*script[\s>]` +
`|javascript\s*:` +
`|vbscript\s*:` +
`|\bon\w+\s*=` +
`|<\s*(iframe|object|embed|applet|form|input|button|link|meta|base)[\s>]` +
`|document\s*\.` +
`|window\s*\.location` +
`|alert\s*\(` +
`|eval\s*\(` +
`)`,
)
pathTraversalRe = regexp.MustCompile(
`(?i)(` +
`\.\.[/\\]` +
`|%2e%2e[%2f%5c/\\]` +
`|\.\.%2f` +
`|\.\.%5c` +
`|%252e%252e` +
`|/etc/passwd` +
`|/etc/shadow` +
`|/proc/self` +
`|\\windows\\system32` +
`)`,
)
cmdInjRe = regexp.MustCompile(
"(?i)(" +
";\\s*(?:ls|cat|id|whoami|uname|pwd|wget|curl|bash|sh|python|perl|ruby|nc|ncat)(?:\\W|$)" +
"|\\|\\s*(?:ls|cat|id|whoami|bash|sh)(?:\\W|$)" +
"|&&\\s*(?:ls|cat|id|whoami|rm|wget|curl)(?:\\W|$)" +
"|`[^`]{1,100}`" +
"|\\$\\([^)]{1,100}\\)" +
")",
)
)
// Check inspects the given inputs against the active WAF rules.
// Returns a BlockReason if the request should be blocked, nil otherwise.
func Check(cfg WAFConfig, rawPath, rawQuery, body string) *BlockReason {
if !cfg.Enabled {
return nil
}
// URL-decode for better detection
decodedPath, _ := url.QueryUnescape(rawPath)
decodedQuery, _ := url.QueryUnescape(rawQuery)
decodedBody, _ := url.QueryUnescape(body)
targets := []string{
strings.ToLower(decodedPath),
strings.ToLower(decodedQuery),
strings.ToLower(decodedBody),
}
for _, input := range targets {
if input == "" {
continue
}
if cfg.BlockPathTraversal {
if m := pathTraversalRe.FindString(input); m != "" {
return &BlockReason{Rule: "path-traversal", Matched: m}
}
}
if cfg.BlockSQLi {
if m := sqliRe.FindString(input); m != "" {
return &BlockReason{Rule: "sqli", Matched: m}
}
}
if cfg.BlockXSS {
if m := xssRe.FindString(input); m != "" {
return &BlockReason{Rule: "xss", Matched: m}
}
}
if cfg.BlockCommandInj {
if m := cmdInjRe.FindString(input); m != "" {
return &BlockReason{Rule: "cmdinj", Matched: m}
}
}
}
return nil
}
|