miguan v1

This commit is contained in:
tamakyi
2026-10-21 12:09:28 +08:00
parent 2d4609dc0e
commit 79051acae8
28 changed files with 2128 additions and 182 deletions
+38 -3
View File
@@ -26,11 +26,13 @@ import (
"github.com/tamakyi/TamaBox/internal/captcha"
"github.com/tamakyi/TamaBox/internal/conf"
"github.com/tamakyi/TamaBox/internal/context"
"github.com/tamakyi/TamaBox/internal/db"
"github.com/tamakyi/TamaBox/internal/form"
"github.com/tamakyi/TamaBox/internal/i18n"
"github.com/tamakyi/TamaBox/internal/mail"
"github.com/tamakyi/TamaBox/internal/route/chat" // 私聊路由
"github.com/tamakyi/TamaBox/internal/security/autoban"
"github.com/tamakyi/TamaBox/internal/security/honeypot"
"github.com/tamakyi/TamaBox/internal/security/ipinfo"
"github.com/tamakyi/TamaBox/internal/shared"
templatepkg "github.com/tamakyi/TamaBox/internal/template"
@@ -96,6 +98,19 @@ func New() *flamego.Flame {
},
})
// 蜜罐(虚假后台登录界面):默认关闭,需在 [security] 段显式 enable_honeypot = true。
// 命中即返回假登录页 + 记录破解凭据 + 复用上面的 autoban 封禁/告警链路。
hp := honeypot.New(honeypot.Options{
Enabled: conf.Security.EnableHoneypot,
ExtraPaths: honeypotExtraPaths(conf.Security.HoneypotPaths),
})
hph := newHoneypotHandler(hp, ab, db.Honeypots)
// 蜜罐告警:只有真的提交了凭据才发(纯路径探测没有取证价值),且每个 IP 只发一次。
// 收件人复用 security_alert_email,不新增配置项。
hph.sendAlert = func(a mail.HoneypotAlert) error {
return mail.SendHoneypotAlertMail(a)
}
templateFS, err := template.EmbedFS(templates.FS, ".", []string{".html"})
if err != nil {
logrus.WithError(err).Fatal("Failed to embed templates file system")
@@ -148,7 +163,7 @@ func New() *flamego.Flame {
f.Use(securityHeaders())
f.Use(ipBlacklistMiddleware())
f.Use(autobanMiddleware(ab))
f.Use(autobanMiddleware(ab, hp))
// 全局请求体上限:必须在任何会读取 body 的中间件(尤其 Contexter 内的 CSRF 校验)
// 之前执行,否则超大 multipart 会在 Bind 的路由级限制生效前落盘临时文件耗尽磁盘。
f.Use(requestBodyLimitMiddleware())
@@ -430,8 +445,28 @@ func New() *flamego.Flame {
context.Contexter(),
)
f.NotFound(func(ctx flamego.Context) {
// 真正的 404 探测在此记录(不把业务 302→/ 误判为扫描)。flamego 的 NotFound 会先
// 写 404 状态再 Redirect,故须在 Redirect 前记录。
// ── 蜜罐分流(优先)───────────────────────────────────────────────
// 命中诱饵路径(/wp-admin、/.ssh、/.env 等后台探测特征)时,返回一个
// 仿 WordPress 的假登录页,并把攻击者提交的凭据记录入库。
//
// 为什么挂在 NotFound 而不是全局中间件:NotFound 只在「路由表完全落空」
// 时触发,因此与 /login、/register、/_/{domain}、/user/**、/api/v1/** 等
// 真实业务路由天然零交集,不会劫持正常用户;且它位于全局 f.Use 链之后,
// requestBodyLimitMiddleware 已对 body 施加上限,解析 POST body 安全。
// 组级中间件(含 CSRF)只挂在具体路由上,不作用于 NotFound,故表单可解析。
//
// 短路:flamego 的 context.run() 在 ResponseWriter().Written() 为真时即
// return,故此处写完响应且不调用 ctx.Next() 即可安全终止整条链路。
if hp != nil && hp.Match(ctx.Request().URL.Path) {
hph.Serve(ctx)
return
}
// 真正的 404 探测在此记录(不把业务 302→/ 误判为扫描)。
// ⚠️ 更正:flamego 的 NotFound 处理器并不会自动写 404 状态码 ——
// ResponseWriter.Write() 在未显式 WriteHeader 时写的是 200,
// ctx.Redirect("/") 写的是 302 + Location: /。这里保持原有 302→/ 行为
// (对扫描器而言,302 到首页同样表示「此路径不存在」)。
// 键必须与 autobanMiddleware 的 IsBanned/Record 一致(同样经 LimitKey 聚合),
// 否则计数落在另一个键上,封禁永不生效。
// ⚠️ 计数受 ban_track_notfound 控制,默认关闭;Record 内部会忽略未开启的