feat:custom info

#352
This commit is contained in:
samwaf
2025-06-05 13:59:20 +08:00
parent 2f8117f08c
commit 9fdd5ee3ce
4 changed files with 126 additions and 85 deletions

View File

@@ -93,7 +93,11 @@ type CacheConfig struct {
// StaticSiteConfig 静态站点配置
type StaticSiteConfig struct {
IsEnableStaticSite int `json:"is_enable_static_site"` // 是否开启静态站点 1开启 0关闭
StaticSitePath string `json:"static_site_path"` // 静态站点路径
StaticSitePrefix string `json:"static_site_prefix"` // 静态站点URL前缀默认为"/"
IsEnableStaticSite int `json:"is_enable_static_site"` // 是否开启静态站点 1开启 0关闭
StaticSitePath string `json:"static_site_path"` // 静态站点路径
StaticSitePrefix string `json:"static_site_prefix"` // 静态站点URL前缀默认为"/"
SensitivePaths string `json:"sensitive_paths"` // 敏感路径列表,逗号分隔
SensitiveExtensions string `json:"sensitive_extensions"` // 敏感文件扩展名,逗号分隔
AllowedExtensions string `json:"allowed_extensions"` // 允许的文件扩展名白名单,逗号分隔
SensitivePatterns string `json:"sensitive_patterns"` // 敏感文件名模式(正则表达式),逗号分隔
}

View File

@@ -186,13 +186,23 @@ func pathCoreSql(db *gorm.DB) {
}
//20250604 静态网站配置初始化
defaultStaticSiteConfig := `{"is_enable_static_site":0,"static_site_path":"","static_site_prefix":"/"}`
defaultStaticSiteConfig := `{"is_enable_static_site":0,"static_site_path":"","static_site_prefix":"/","sensitive_paths":"/etc/passwd,/etc/shadow,/etc/group,/etc/gshadow,/etc/hosts,/etc/hostname,/etc/resolv.conf,/etc/ssh/,/var/log/,/.ssh/,/.bash_history,/.profile,/.bashrc,/etc/crontab,/var/spool/cron/,/etc/apache2/,/etc/nginx/,/etc/httpd/,/var/www/,/usr/share/,/var/tmp/,/var/run/,c:\\windows\\,c:\\program files\\,c:\\program files (x86)\\,c:\\users\\,c:\\documents and settings\\,c:\\windows\\system32\\,c:\\windows\\syswow64\\,c:\\boot.ini,c:\\autoexec.bat,c:\\config.sys,\\windows\\,\\program files\\,\\program files (x86)\\,\\users\\,\\documents and settings\\,\\windows\\system32\\,\\windows\\syswow64\\,boot.ini,autoexec.bat,config.sys,ntuser.dat,pagefile.sys,hiberfil.sys,swapfile.sys","sensitive_extensions":".key,.pem,.crt,.p12,.pfx,.jks,.bak,.backup,.old,.orig,.save,.sql,.db,.sqlite,.mdb,.env,.htaccess,.htpasswd,.git,.svn,.hg,.bzr,.DS_Store,Thumbs.db,desktop.ini,.tmp,.temp,.lock,.pid","allowed_extensions":".html,.htm,.css,.js,.json,.png,.jpg,.jpeg,.gif,.svg,.ico,.webp,.pdf,.txt,.md,.xml,.woff,.woff2,.ttf,.eot,.mp4,.webm,.ogg,.mp3,.wav,.zip,.tar,.gz,.rar","sensitive_patterns":"(?i)\\.git(/|\\\\),(?i)\\.svn(/|\\\\),(?i)\\.env,(?i)database\\.(php|xml|json|yaml|yml),(?i)(backup|dump|export)\\.(sql|db|tar|zip|gz),(?i)(id_rsa|id_dsa|id_ecdsa|id_ed25519),(?i)\\.ssh(/|\\\\).*,(?i)(access|error|debug)\\.log,(?i)web\\.config,(?i)phpinfo\\.php"}`
// 处理static_site_json为null的情况
err = db.Exec("UPDATE hosts SET static_site_json=? WHERE static_site_json IS NULL", defaultStaticSiteConfig).Error
if err != nil {
panic("failed to hosts :static_site_json " + err.Error())
} else {
zlog.Info("db", "hosts :static_site_json init successfully")
}
// 处理static_site_json不包含sensitive_paths字段的情况
err = db.Exec("UPDATE hosts SET static_site_json=? WHERE static_site_json IS NOT NULL AND static_site_json NOT LIKE '%sensitive_paths%'", defaultStaticSiteConfig).Error
if err != nil {
panic("failed to hosts :static_site_json sensitive_paths update " + err.Error())
} else {
zlog.Info("db", "hosts :static_site_json sensitive_paths update successfully")
}
// 记录结束时间并计算耗时
duration := time.Since(startTime)
zlog.Info("create core default value completely", "duration", duration.String())

View File

@@ -18,56 +18,6 @@ import (
// 敏感文件和路径黑名单
var (
// Linux 敏感路径和文件
linuxSensitivePaths = []string{
"/etc/passwd", "/etc/shadow", "/etc/group", "/etc/gshadow",
"/etc/hosts", "/etc/hostname", "/etc/resolv.conf",
"/etc/ssh/", "/var/log/",
"/.ssh/", "/.bash_history", "/.profile", "/.bashrc",
"/etc/crontab", "/var/spool/cron/",
"/etc/apache2/", "/etc/nginx/", "/etc/httpd/",
"/var/www/", "/usr/share/",
"/var/tmp/", "/var/run/",
}
// Windows 敏感路径和文件
windowsSensitivePaths = []string{
"c:\\windows\\", "c:\\program files\\", "c:\\program files (x86)\\",
"c:\\users\\", "c:\\documents and settings\\",
"c:\\windows\\system32\\", "c:\\windows\\syswow64\\",
"c:\\boot.ini", "c:\\autoexec.bat", "c:\\config.sys",
"\\windows\\", "\\program files\\", "\\program files (x86)\\",
"\\users\\", "\\documents and settings\\",
"\\windows\\system32\\", "\\windows\\syswow64\\",
"boot.ini", "autoexec.bat", "config.sys",
"ntuser.dat", "pagefile.sys", "hiberfil.sys", "swapfile.sys",
}
// 敏感文件扩展名
sensitiveExtensions = []string{
".key", ".pem", ".crt", ".p12", ".pfx", ".jks",
".bak", ".backup", ".old", ".orig", ".save",
".sql", ".db", ".sqlite", ".mdb",
".env", ".htaccess", ".htpasswd",
".git", ".svn", ".hg", ".bzr",
".DS_Store", "Thumbs.db", "desktop.ini",
".tmp", ".temp", ".lock", ".pid",
}
// 敏感文件名模式
sensitiveFilePatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)\.git(/|\\)`),
regexp.MustCompile(`(?i)\.svn(/|\\)`),
regexp.MustCompile(`(?i)\.env`),
regexp.MustCompile(`(?i)database\.(php|xml|json|yaml|yml)`),
regexp.MustCompile(`(?i)(backup|dump|export)\.(sql|db|tar|zip|gz)`),
regexp.MustCompile(`(?i)(id_rsa|id_dsa|id_ecdsa|id_ed25519)`),
regexp.MustCompile(`(?i)\.ssh(/|\\).*`),
regexp.MustCompile(`(?i)(access|error|debug)\.log`),
regexp.MustCompile(`(?i)web\.config`),
regexp.MustCompile(`(?i)phpinfo\.php`),
}
// 路径穿越攻击模式
pathTraversalPatterns = []*regexp.Regexp{
regexp.MustCompile(`\.\.[\\/]`),
@@ -84,6 +34,77 @@ var (
}
)
// splitConfigString 分割配置字符串为数组,去除空白项
func splitConfigString(configStr string) []string {
if configStr == "" {
return []string{}
}
parts := strings.Split(configStr, ",")
result := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" {
result = append(result, part)
}
}
return result
}
// getDefaultSensitivePathsString 获取默认敏感路径配置字符串
func getDefaultSensitivePathsString() string {
return "/etc/passwd,/etc/shadow,/etc/group,/etc/gshadow,/etc/hosts,/etc/hostname,/etc/resolv.conf,/etc/ssh/,/var/log/,/.ssh/,/.bash_history,/.profile,/.bashrc,/etc/crontab,/var/spool/cron/,/etc/apache2/,/etc/nginx/,/etc/httpd/,/var/www/,/usr/share/,/var/tmp/,/var/run/,c:\\windows\\,c:\\program files\\,c:\\program files (x86)\\,c:\\users\\,c:\\documents and settings\\,c:\\windows\\system32\\,c:\\windows\\syswow64\\,c:\\boot.ini,c:\\autoexec.bat,c:\\config.sys,\\windows\\,\\program files\\,\\program files (x86)\\,\\users\\,\\documents and settings\\,\\windows\\system32\\,\\windows\\syswow64\\,boot.ini,autoexec.bat,config.sys,ntuser.dat,pagefile.sys,hiberfil.sys,swapfile.sys"
}
// getDefaultSensitiveExtensionsString 获取默认敏感文件扩展名字符串
func getDefaultSensitiveExtensionsString() string {
return ".key,.pem,.crt,.p12,.pfx,.jks,.bak,.backup,.old,.orig,.save,.sql,.db,.sqlite,.mdb,.env,.htaccess,.htpasswd,.git,.svn,.hg,.bzr,.DS_Store,Thumbs.db,desktop.ini,.tmp,.temp,.lock,.pid"
}
// getDefaultAllowedExtensionsString 获取默认允许的文件扩展名字符串
func getDefaultAllowedExtensionsString() string {
return ".html,.htm,.css,.js,.json,.png,.jpg,.jpeg,.gif,.svg,.ico,.webp,.pdf,.txt,.md,.xml,.woff,.woff2,.ttf,.eot,.mp4,.webm,.ogg,.mp3,.wav,.zip,.tar,.gz,.rar"
}
// getDefaultSensitivePatternsString 获取默认敏感文件名模式字符串
func getDefaultSensitivePatternsString() string {
return `(?i)\.git(/|\\),(?i)\.svn(/|\\),(?i)\.env,(?i)database\.(php|xml|json|yaml|yml),(?i)(backup|dump|export)\.(sql|db|tar|zip|gz),(?i)(id_rsa|id_dsa|id_ecdsa|id_ed25519),(?i)\.ssh(/|\\).*,(?i)(access|error|debug)\.log,(?i)web\.config,(?i)phpinfo\.php`
}
// getSensitivePathsFromConfig 从配置获取敏感路径数组
func getSensitivePathsFromConfig(config model.StaticSiteConfig) []string {
if config.SensitivePaths != "" {
return splitConfigString(config.SensitivePaths)
}
return splitConfigString(getDefaultSensitivePathsString())
}
// getSensitiveExtensionsFromConfig 从配置获取敏感扩展名数组
func getSensitiveExtensionsFromConfig(config model.StaticSiteConfig) []string {
if config.SensitiveExtensions != "" {
return splitConfigString(config.SensitiveExtensions)
}
return splitConfigString(getDefaultSensitiveExtensionsString())
}
// getAllowedExtensionsFromConfig 从配置获取允许扩展名数组
func getAllowedExtensionsFromConfig(config model.StaticSiteConfig) []string {
if config.AllowedExtensions != "" {
return splitConfigString(config.AllowedExtensions)
}
return splitConfigString(getDefaultAllowedExtensionsString())
}
// getSensitivePatternsFromConfig 从配置获取敏感模式数组
func getSensitivePatternsFromConfig(config model.StaticSiteConfig) []string {
if config.SensitivePatterns != "" {
return splitConfigString(config.SensitivePatterns)
}
return splitConfigString(getDefaultSensitivePatternsString())
}
// serveStaticFile 提供静态文件服务
func (waf *WafEngine) serveStaticFile(w http.ResponseWriter, r *http.Request, config model.StaticSiteConfig, weblog *innerbean.WebLog, hostsafe *wafenginmodel.HostSafe) bool {
// 记录访问尝试
@@ -133,7 +154,7 @@ func (waf *WafEngine) serveStaticFile(w http.ResponseWriter, r *http.Request, co
}
// 敏感路径检查
if waf.isSensitivePath(relativePath) {
if waf.isSensitivePath(relativePath, config) {
waf.logSecurityEvent("访问敏感路径", originalPath, r.RemoteAddr, "", weblog)
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("403 Forbidden: Access to sensitive path denied"))
@@ -141,7 +162,7 @@ func (waf *WafEngine) serveStaticFile(w http.ResponseWriter, r *http.Request, co
}
// 敏感文件检查
if waf.isSensitiveFile(relativePath) {
if waf.isSensitiveFile(relativePath, config) {
waf.logSecurityEvent("访问敏感文件", originalPath, r.RemoteAddr, "", weblog)
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("403 Forbidden: Access to sensitive file denied"))
@@ -216,7 +237,7 @@ func (waf *WafEngine) serveStaticFile(w http.ResponseWriter, r *http.Request, co
}
// 检查文件类型(基于扩展名)
if !waf.isAllowedFileType(absFullPath) {
if !waf.isAllowedFileType(absFullPath, config) {
waf.logSecurityEvent("不允许的文件类型", originalPath, r.RemoteAddr, "", weblog)
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("403 Forbidden: File type not allowed"))
@@ -285,18 +306,14 @@ func (waf *WafEngine) containsEncodedThreats(filePath string) bool {
}
// isSensitivePath 检查是否为敏感路径
func (waf *WafEngine) isSensitivePath(filePath string) bool {
func (waf *WafEngine) isSensitivePath(filePath string, config model.StaticSiteConfig) bool {
lowerPath := strings.ToLower(filePath)
// 检查Linux敏感路径
for _, sensitivePath := range linuxSensitivePaths {
if strings.Contains(lowerPath, strings.ToLower(sensitivePath)) {
return true
}
}
// 从配置获取敏感路径数组
sensitivePaths := getSensitivePathsFromConfig(config)
// 检查Windows敏感路径
for _, sensitivePath := range windowsSensitivePaths {
// 检查敏感路径
for _, sensitivePath := range sensitivePaths {
if strings.Contains(lowerPath, strings.ToLower(sensitivePath)) {
return true
}
@@ -306,10 +323,13 @@ func (waf *WafEngine) isSensitivePath(filePath string) bool {
}
// isSensitiveFile 检查是否为敏感文件
func (waf *WafEngine) isSensitiveFile(filePath string) bool {
func (waf *WafEngine) isSensitiveFile(filePath string, config model.StaticSiteConfig) bool {
lowerPath := strings.ToLower(filePath)
fileName := filepath.Base(lowerPath)
// 从配置获取敏感扩展名数组
sensitiveExtensions := getSensitiveExtensionsFromConfig(config)
// 检查敏感文件扩展名
for _, ext := range sensitiveExtensions {
if strings.HasSuffix(fileName, ext) {
@@ -317,10 +337,15 @@ func (waf *WafEngine) isSensitiveFile(filePath string) bool {
}
}
// 从配置获取敏感模式数组
sensitivePatterns := getSensitivePatternsFromConfig(config)
// 检查敏感文件名模式
for _, pattern := range sensitiveFilePatterns {
if pattern.MatchString(lowerPath) {
return true
for _, patternStr := range sensitivePatterns {
if pattern, err := regexp.Compile(patternStr); err == nil {
if pattern.MatchString(lowerPath) {
return true
}
}
}
@@ -333,16 +358,9 @@ func (waf *WafEngine) isSensitiveFile(filePath string) bool {
}
// isAllowedFileType 检查是否为允许的文件类型
func (waf *WafEngine) isAllowedFileType(filePath string) bool {
// 允许的文件扩展名白名单
allowedExtensions := []string{
".html", ".htm", ".css", ".js", ".json",
".png", ".jpg", ".jpeg", ".gif", ".svg", ".ico", ".webp",
".pdf", ".txt", ".md", ".xml",
".woff", ".woff2", ".ttf", ".eot",
".mp4", ".webm", ".ogg", ".mp3", ".wav",
".zip", ".tar", ".gz", ".rar",
}
func (waf *WafEngine) isAllowedFileType(filePath string, config model.StaticSiteConfig) bool {
// 从配置获取允许扩展名数组
allowedExtensions := getAllowedExtensionsFromConfig(config)
ext := strings.ToLower(filepath.Ext(filePath))
if ext == "" {
@@ -416,3 +434,16 @@ func (waf *WafEngine) logSecurityEvent(eventType, path, remoteAddr, details stri
zap.String("remote_addr", remoteAddr),
zap.String("details", details))
}
// 初始化静态站点配置的默认值
func InitDefaultStaticSiteConfig() model.StaticSiteConfig {
return model.StaticSiteConfig{
IsEnableStaticSite: 0,
StaticSitePath: "",
StaticSitePrefix: "/",
SensitivePaths: getDefaultSensitivePathsString(),
SensitiveExtensions: getDefaultSensitiveExtensionsString(),
AllowedExtensions: getDefaultAllowedExtensionsString(),
SensitivePatterns: getDefaultSensitivePatternsString(),
}
}

View File

@@ -189,11 +189,7 @@ func (waf *WafEngine) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
// 静态站点服务检查
staticSiteConfig := model.StaticSiteConfig{
IsEnableStaticSite: 0,
StaticSitePath: "",
StaticSitePrefix: "/",
}
staticSiteConfig := InitDefaultStaticSiteConfig()
err := json.Unmarshal([]byte(hostTarget.Host.StaticSiteJSON), &staticSiteConfig)
if err != nil {
zlog.Debug("解析static site json失败")