fix(security): 修复 P2 中危项(cookie/协议限速/路径遍历/默认口令/中继TLS/安全头/信息泄露)
- 会话 cookie 增加 Secure 标志;新增 [web].cookie_secure 配置 (默认 true,仅本地 HTTP 调试关闭;缺失字段按安全默认处理) - SMTP/IMAP/POP3 认证接入封禁体系(store.RecordAuthFailure 与 Web 共用 ban_entries):失败计数达 ban.max_fail_attempts 即封禁 IP, 已封禁 IP 拒绝认证,堵住协议层暴力破解 - 附件存储路径遍历防护重写:FullPath 白名单校验(UUID 文件名格式) + baseDir 前缀兜底,非法路径返回错误;Save 扩展名白名单化 - 初始管理员不再使用 admin/admin:密码取 MAILGO_ADMIN_PASSWORD 或 随机生成并打印一次;新增 MustChangePassword 首登强制改密 (管理员重置密码同样触发) - 外发中继默认验证 TLS 证书(保护 AUTH 凭据,防 MITM),直投 MX 保持机会式 TLS;新增 outbound.relay_tls_insecure 开关(默认 false) - 新增安全响应头中间件:HSTS、X-Frame-Options DENY、nosniff、 Referrer-Policy、基础 CSP(frame-ancestors 'none' 防点击劫持, connect-src/form-action 'self' 防数据外泄) - LDAP/OAuth 登录错误统一为通用文案,原始错误只写日志, 不再回显邮箱/内部细节(防用户枚举与信息泄露) - 新增 25 个回归测试:cookie 标志、封禁阈值、路径遍历用例、 中继 TLS 验证(自签证书 STARTTLS 集成)、安全头、OAuth 文案 部署注意:升级后所有会话失效需重新登录;若直接以 HTTP 提供 服务需显式配置 cookie_secure = false。
This commit is contained in:
26 files changed
+799
-101
No files matched your search
@@ -36,13 +36,14 @@ type SMTPServer struct {
|
||||
storage *storage.AttachmentStorage
|
||||
outbound *outbound.Manager
|
||||
cfg config.SMTPConfig
|
||||
banCfg config.BanConfig
|
||||
tlsLoader *tlsutil.Loader
|
||||
}
|
||||
|
||||
// NewSMTPServer creates a new SMTP server instance. tlsLoader may be nil
|
||||
// when TLS is not configured.
|
||||
func NewSMTPServer(cfg config.SMTPConfig, stores *store.Stores, attStorage *storage.AttachmentStorage, ob *outbound.Manager, tlsLoader *tlsutil.Loader) *SMTPServer {
|
||||
return &SMTPServer{stores: stores, storage: attStorage, outbound: ob, cfg: cfg, tlsLoader: tlsLoader}
|
||||
func NewSMTPServer(cfg config.SMTPConfig, stores *store.Stores, attStorage *storage.AttachmentStorage, ob *outbound.Manager, tlsLoader *tlsutil.Loader, banCfg config.BanConfig) *SMTPServer {
|
||||
return &SMTPServer{stores: stores, storage: attStorage, outbound: ob, cfg: cfg, banCfg: banCfg, tlsLoader: tlsLoader}
|
||||
}
|
||||
|
||||
func (s *SMTPServer) tlsConfig() (*tls.Config, error) {
|
||||
@@ -108,9 +109,10 @@ type smtpBackend struct {
|
||||
// NewSession creates a new SMTP session for the incoming connection.
|
||||
func (be *smtpBackend) NewSession(c *smtp.Conn) (smtp.Session, error) {
|
||||
return &smtpSession{
|
||||
backend: be,
|
||||
mode: be.mode,
|
||||
rcpts: make([]string, 0),
|
||||
backend: be,
|
||||
mode: be.mode,
|
||||
rcpts: make([]string, 0),
|
||||
clientIP: store.ClientIPFromAddr(c.Conn().RemoteAddr()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -126,6 +128,7 @@ type smtpSession struct {
|
||||
userID uint
|
||||
email string
|
||||
user *db.User
|
||||
clientIP string
|
||||
}
|
||||
|
||||
// AuthMechanisms returns supported SMTP AUTH mechanisms.
|
||||
@@ -139,8 +142,19 @@ func (s *smtpSession) Auth(mech string) (sasl.Server, error) {
|
||||
return nil, smtp.ErrAuthUnknownMechanism
|
||||
}
|
||||
return sasl.NewPlainServer(func(identity, username, password string) error {
|
||||
// 已封禁 IP 一律拒绝认证(防协议层暴力破解)
|
||||
if banned, _ := s.backend.server.stores.Bans.IsBanned(s.clientIP); banned {
|
||||
return smtp.ErrAuthFailed
|
||||
}
|
||||
|
||||
user, err := s.backend.server.stores.Users.Authenticate(username, password)
|
||||
if err != nil {
|
||||
// 认证失败计数,达到阈值封禁(与 Web 登录共用 ban_entries)
|
||||
s.backend.server.stores.RecordAuthFailure(
|
||||
s.clientIP,
|
||||
s.backend.server.banCfg.MaxFailAttempts,
|
||||
s.backend.server.banCfg.BanDurationMin,
|
||||
)
|
||||
return smtp.ErrAuthFailed
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user