security: waf 边缘统一注入安全响应头

外部扫描发现站点缺少安全响应头。在 waf 反代/403/502 三个出站路径统一注入 XFO/nosniff/Referrer-Policy/Permissions-Policy/HSTS/CSP,下游同名头一律覆盖避免重复;static 补上漏调用的 apply_security_headers 做纵深防御。
This commit is contained in:
qing committed 2026-07-28 09:19:43 +08:00
1 parent e89578fa82
commit b2ddd9e7e3
2 files changed
+25

No files matched your search

+2
View File
@@ -13,6 +13,7 @@ import sys
# pymsg 日志客户端(集中式日志 → dbv2 Logs 表)
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
from log_client import logs # noqa: E402
from security_middleware import apply_security_headers # noqa: E402
# 基准目录:编译后取可执行文件所在目录,开发时取脚本所在目录
# 解决 Nuitka onefile 解压到 /tmp 后相对路径失效的问题
@@ -38,6 +39,7 @@ args = parser.parse_args()
app = Flask(__name__)
CORS(app, origins=["https://qsont.me", "https://yhdet.top", "http://localhost:9200"])
apply_security_headers(app)
# ==================== 配置文件加载 ====================
CONFIG_FILE = args.config
+23
View File
@@ -48,11 +48,33 @@ func New(upstream string, waf *engine.Engine) (*Proxy, error) {
rp.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
logclient.Error("waf", fmt.Sprintf("upstream_error path=%s err=%v", r.URL.Path, err),
map[string]any{"ip": r.RemoteAddr})
setSecurityHeaders(w.Header())
http.Error(w, "upstream unavailable", http.StatusBadGateway)
}
// 统一注入安全响应头:在边缘单点覆盖所有出站响应。
// Header.Set 覆盖下游同名头,不会产生重复字段
rp.ModifyResponse = func(res *http.Response) error {
setSecurityHeaders(res.Header)
return nil
}
return p, nil
}
// setSecurityHeaders 设置安全响应头基线(与 static/security_middleware.py 保持一致)。
// CSP 暂保留 unsafe-inline:forgot.html 与 games/*.html 含内联脚本。
// HSTS 半年期、不含子域,站点全量 HTTPS 后可按需收紧。
func setSecurityHeaders(h http.Header) {
h.Set("X-Frame-Options", "DENY")
h.Set("X-Content-Type-Options", "nosniff")
h.Set("X-XSS-Protection", "1; mode=block")
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
h.Set("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
h.Set("Strict-Transport-Security", "max-age=15552000")
h.Set("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; "+
"img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'")
}
// ServeHTTP 实现 http.Handler。
// 流程:ProcessRequest → 命中则 403 → 否则转发 → ProcessResponseHeaders。
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
@@ -126,6 +148,7 @@ func (p *Proxy) block(w http.ResponseWriter, r *http.Request, ruleID int, start
"ua": r.UserAgent(),
"cost_ms": time.Since(start).Milliseconds(),
})
setSecurityHeaders(w.Header())
w.WriteHeader(http.StatusForbidden)
_, _ = w.Write([]byte("blocked by WAF\n"))
}