修复自身收信地址可绑定外部安全邮箱的问题
Some checks are pending
Security Checks / security (push) Waiting to run

This commit is contained in:
smail 2026-09-26 01:20:49 +08:00
parent 1f359fcaf8
commit 9762300595
2 changed files with 59 additions and 1 deletions

View File

@ -995,8 +995,38 @@ func currentUserPrimaryEmail(userID int, email string) (bool, error) {
if db.Instance == nil || userID <= 0 {
return false, errors.New("database is unavailable")
}
normalizedEmail, err := externalemail.NormalizeEmail(email)
if err != nil {
return false, err
}
var binding models.UserEmail
return db.Instance.Where("user_id = ? AND verified_at IS NOT NULL AND status != ? AND is_primary = 1 AND (address_canonical = ? OR LOWER(address) = ?)", userID, 3, email, email).Limit(1).Get(&binding)
owned, err := db.Instance.Where("user_id = ? AND verified_at IS NOT NULL AND status != ? AND is_primary = 1 AND (address_canonical = ? OR LOWER(address) = ?)", userID, 3, normalizedEmail, normalizedEmail).Limit(1).Get(&binding)
if err != nil || owned {
return owned, err
}
// 本地收信按账号和域名路由,新建账号不一定存在独立的主邮箱绑定记录。
parts := strings.SplitN(normalizedEmail, "@", 2)
domains := config.CurrentMailDomains()
localDomain := strings.EqualFold(parts[1], domains.Primary)
for _, domain := range domains.Accepted {
if strings.EqualFold(parts[1], domain) {
localDomain = true
break
}
}
if !localDomain {
return false, nil
}
var user models.User
found, err := db.Instance.ID(userID).Get(&user)
if err != nil {
return false, err
}
if !found {
return false, errors.New("user is unavailable")
}
return strings.EqualFold(parts[0], user.Account), nil
}
func VerifyExternalEmail(ctx *context.Context, w http.ResponseWriter, req *http.Request) {
@ -1010,6 +1040,16 @@ func VerifyExternalEmail(ctx *context.Context, w http.ResponseWriter, req *http.
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
// 确认时重新检查归属,拒绝修复前创建或域名配置变化后的自身邮箱挑战。
owned, err := currentUserPrimaryEmail(ctx.UserID, input.Email)
if err != nil {
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
if owned {
writeSecurityError(w, response.ParamsError, "不能将当前邮箱绑定为外部安全邮箱")
return
}
if err := runtime.verification.VerifyBound(input.ID, ctx.UserAccount, input.Email, input.Code, verification.PurposeExternalEmailBind, input.BindingID, remoteIP(req)); err != nil {
writeSecurityError(w, response.ParamsError, externalEmailVerificationCheckMessage(err))
return
@ -1038,6 +1078,15 @@ func VerifyOwnExternalEmail(ctx *context.Context, w http.ResponseWriter, req *ht
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
owned, err := currentUserPrimaryEmail(ctx.UserID, normalizedEmail)
if err != nil {
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
if owned {
writeSecurityError(w, response.ParamsError, "不能将当前邮箱绑定为外部安全邮箱")
return
}
if err := runtime.verification.VerifyBound(
input.ID,
ctx.UserAccount,

View File

@ -301,6 +301,15 @@ func VerifyManagedExternalEmail(ctx *context.Context, w http.ResponseWriter, req
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
owned, err := currentUserPrimaryEmail(target.ID, normalizedEmail)
if err != nil {
writeSecurityError(w, response.ParamsError, "验证失败")
return
}
if owned {
writeSecurityError(w, response.ParamsError, "不能将当前邮箱绑定为外部安全邮箱")
return
}
if err := runtime.verification.VerifyBound(
input.ChallengeID,
target.Account,