Files
samwaf 24227923e8 feat: strengthen WAF detection coverage and gate the new inspection paths
Detection engine:
- Normalize request data (body, cookies, form and JSON values, request headers)
  before inspection instead of only the URL query string, so encoded variants
  are evaluated consistently.
- Extend XSS / SQLi / command-injection inspection to request bodies and to
  custom request headers; bodies and headers are scanned per value rather than
  as one blob to keep false positives low.
- Add a configurable body inspection mode (body_detect_mode: observe/block/off,
  default observe) so the new deep checks record rather than block until an
  operator confirms them, plus body_detect_field_exclude to skip fields that
  legitimately carry rich text.
- Keep false positives down on the new paths: skip structured values, require a
  corroborating signal before flagging body/header XSS, and leave dual-use
  keywords out of the fallback lists.
- Path traversal is now normalized and judged on whether it escapes the site
  root instead of matching literally; this also clears a long-standing false
  positive on legitimate in-site relative paths. Adds a sensitive-file list.
- Scanner detection now covers all request headers and known probe paths.

Plugins:
- Suspend plugin loading pending signing and admission work. The code path is
  retained but gated off, and shipped disabled by default.

Validated against a local test corpus and a 33k real-traffic benchmark:
detection improved across every category while the real-traffic false-positive
count stayed flat.
2026-08-23 11:29:22 +08:00

65 lines
2.3 KiB
Go

package libinjection
import (
"SamWaf/innerbean"
"strings"
)
// 独特工具指纹:在 URL 或 User-Agent 命中即判定。
var scannerSigsAny = []string{
"sqlmap", "nikto", "nessus", "acunetix", "appscan",
"masscan", "wpscan", "gobuster", "dirbuster", "feroxbuster",
"ffuf", "wfuzz", "zgrab", "openvas", "w3af",
"skipfish", "whatweb", "netsparker",
}
// 像普通英文/科学词的工具名(nuclei/nmap/arachni):只在 UA 里判,避免正常 URL/内容误伤。
var scannerSigsUA = []string{"nuclei", "nmap", "arachni"}
// D2-6:扫描器注入的特定头名/标记(对标 CRS 913110 scanners-headers)。均为扫描器特有、正常
// 流量不出现,命中即判。acunetix/netsparker 等厂商名已由 scannerSigsAny 覆盖(查全部头)。
var scannerHeaderMarkers = []string{
"x-scan-memo", "x-scanner", "x-wipp", "x-ratproxy-loop", "wvstest", "x-wf-scanner",
}
// D2-7:扫描器探测路径/文件名指纹(对标 CRS 913120 scanners-urls)。不含工具名、但为已知扫描
// 探测串。含工具名的探测路径(acunetix-wvs-test 等)已由 scannerSigsAny 覆盖。
var scannerUrlProbes = []string{
"w00tw00t", "muieblackcat", "thereisnowaythat-you-canbe-a-search-engine", "cybercop",
}
// IsScan 检测已知扫描/攻击工具。工具名查 URL+全部请求头(含 UA、自定义头)、大小写不敏感;
// 另叠扫描器头名标记(D2-6)与探测路径指纹(D2-7)。未纳入 curl/python-requests/Go-http-client
// 等常见客户端,避免误伤。
func IsScan(log *innerbean.WebLog) bool {
url := strings.ToLower(log.URL)
hdr := strings.ToLower(log.HEADER) // joinHeader 结果,含 UA 与自定义头(扫描器工具名常塞头里)
// 工具名:URL + 全部请求头(D2-6 闭合 header 位缺口)
hay := url + "\n" + hdr + "\n" + strings.ToLower(log.USER_AGENT)
for _, s := range scannerSigsAny {
if strings.Contains(hay, s) {
return true
}
}
// 易误伤的短工具名(nmap/nuclei/arachni):仅 UA
ua := strings.ToLower(log.USER_AGENT)
for _, s := range scannerSigsUA {
if strings.Contains(ua, s) {
return true
}
}
// D2-6 扫描器头名标记
for _, s := range scannerHeaderMarkers {
if strings.Contains(hdr, s) {
return true
}
}
// D2-7 扫描器探测路径指纹
for _, s := range scannerUrlProbes {
if strings.Contains(url, s) {
return true
}
}
return false
}