P0(高危): - 新增全局 CSRF 中间件(同步器令牌),覆盖全部 30 个表单与 AJAX 请求 - 修复附件上传/列表/删除越权(IDOR),增加 admin/上传者/文章作者所有权校验 - 登录/注册成功后会话轮换,修复会话固定 - 会话密钥改用 crypto/rand 生成,配置缺失 secret 时拒绝启动 P1(中危): - session 与 comment_uid cookie 增加 Secure/SameSite 标志 - 新增安全响应头:CSP、X-Content-Type-Options、X-Frame-Options、HSTS 等 - 新增 web.trusted_proxies 配置,修复 X-Forwarded-For 伪造 - 修复浏览量记录 goroutine 访问已回收 gin.Context 的数据竞争 补充 17 个安全回归测试(middleware/handlers),go test -race 全绿
77 lines
2.4 KiB
Go
77 lines
2.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// DefaultData builds a base gin.H map populated with values set by the
|
|
// SetUserContext middleware (translations, language, auth state). Handlers
|
|
// add page-specific fields on top and pass it to c.HTML().
|
|
func DefaultData(c *gin.Context) gin.H {
|
|
tr, _ := c.Get("tr")
|
|
isLoggedIn, _ := c.Get("is_logged_in")
|
|
username, _ := c.Get("username")
|
|
lang, _ := c.Get("lang")
|
|
switchLang, _ := c.Get("switch_lang")
|
|
avatar, _ := c.Get("avatar")
|
|
displayName, _ := c.Get("display_name")
|
|
role, _ := c.Get("role")
|
|
siteSetting, _ := c.Get("site_setting")
|
|
siteLogo, _ := c.Get("site_logo")
|
|
siteLogoIsURL, _ := c.Get("site_logo_is_url")
|
|
siteFavicon, _ := c.Get("site_favicon")
|
|
siteFaviconIsURL, _ := c.Get("site_favicon_is_url")
|
|
siteLogoText, _ := c.Get("site_logo_text")
|
|
siteHeaderText, _ := c.Get("site_header_text")
|
|
siteHomeWelcome, _ := c.Get("site_home_welcome")
|
|
siteHomeSubtitle, _ := c.Get("site_home_subtitle")
|
|
siteFooterText, _ := c.Get("site_footer_text")
|
|
navLinks, _ := c.Get("nav_links")
|
|
csrfToken, _ := c.Get("csrf_token")
|
|
|
|
return gin.H{
|
|
"Tr": tr,
|
|
"IsLoggedIn": isLoggedIn,
|
|
"Username": username,
|
|
"Lang": lang,
|
|
"SwitchLang": switchLang,
|
|
"Avatar": avatar,
|
|
"DisplayName": displayName,
|
|
"Role": role,
|
|
"SiteSetting": siteSetting,
|
|
"SiteLogo": siteLogo,
|
|
"SiteLogoIsURL": siteLogoIsURL,
|
|
"SiteFavicon": siteFavicon,
|
|
"SiteFaviconIsURL": siteFaviconIsURL,
|
|
"SiteLogoText": siteLogoText,
|
|
"SiteHeaderText": siteHeaderText,
|
|
"SiteHomeWelcome": siteHomeWelcome,
|
|
"SiteHomeSubtitle": siteHomeSubtitle,
|
|
"SiteFooterText": siteFooterText,
|
|
"NavLinks": navLinks,
|
|
"CSRFToken": csrfToken,
|
|
}
|
|
}
|
|
|
|
// getTr is a convenience helper that returns the translation map from the
|
|
// Gin context, falling back to an empty map if not set.
|
|
func getTr(c *gin.Context) map[string]string {
|
|
tr, ok := c.Get("tr")
|
|
if !ok {
|
|
return map[string]string{}
|
|
}
|
|
m, ok := tr.(map[string]string)
|
|
if !ok {
|
|
return map[string]string{}
|
|
}
|
|
return m
|
|
}
|
|
|
|
// GetClientIP returns the real client IP. It relies on gin's proxy-aware
|
|
// ClientIP(), which honors the trusted_proxies config: only IPs in that list
|
|
// are allowed to influence X-Forwarded-For, so direct clients cannot spoof
|
|
// the value.
|
|
func GetClientIP(c *gin.Context) string {
|
|
return c.ClientIP()
|
|
}
|