Security fixes
1. Password reset tokens: use crypto/rand instead of math/rand 2. IP spoofing: only trust X-Forwarded-For from configured trusted proxies 3. Cleanup: purge failed login attempts older than 24 hours 4. JWT secret: validate minimum 32 character length on startup Updated .env.example with TRUSTED_PROXIES setting
This commit is contained in:
parent
ccaa1eca7e
commit
269fb6f53a
6 changed files with 82 additions and 46 deletions
|
|
@ -15,16 +15,18 @@ import (
|
|||
const MaxLoginAttempts = 5
|
||||
|
||||
type AuthHandler struct {
|
||||
db *db.DB
|
||||
jwtManager *auth.JWTManager
|
||||
emailService *mail.EmailService
|
||||
db *db.DB
|
||||
jwtManager *auth.JWTManager
|
||||
emailService *mail.EmailService
|
||||
trustedProxies []string
|
||||
}
|
||||
|
||||
func NewAuthHandler(database *db.DB, jwtManager *auth.JWTManager, emailService *mail.EmailService) *AuthHandler {
|
||||
func NewAuthHandler(database *db.DB, jwtManager *auth.JWTManager, emailService *mail.EmailService, trustedProxies []string) *AuthHandler {
|
||||
return &AuthHandler{
|
||||
db: database,
|
||||
jwtManager: jwtManager,
|
||||
emailService: emailService,
|
||||
db: database,
|
||||
jwtManager: jwtManager,
|
||||
emailService: emailService,
|
||||
trustedProxies: trustedProxies,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +58,9 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
ip := getClientIP(c)
|
||||
h.cleanupOldAttempts()
|
||||
|
||||
ip := h.getClientIP(c)
|
||||
|
||||
if isLockedOut(ip, req.Username, h.db) {
|
||||
Error(c, http.StatusTooManyRequests, "too many failed attempts, try again later")
|
||||
|
|
@ -241,10 +245,24 @@ func clearFailedAttempts(identifier, ip string, database *db.DB) {
|
|||
Update("successful", true)
|
||||
}
|
||||
|
||||
func getClientIP(c *gin.Context) string {
|
||||
forwarded := c.GetHeader("X-Forwarded-For")
|
||||
if forwarded != "" {
|
||||
return strings.Split(forwarded, ",")[0]
|
||||
}
|
||||
return c.ClientIP()
|
||||
func (h *AuthHandler) cleanupOldAttempts() {
|
||||
cutoff := time.Now().Add(-24 * time.Hour)
|
||||
h.db.Where("attempted_at < ? AND successful = false", cutoff).Delete(&db.ImcLoginAttempt{})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) getClientIP(c *gin.Context) string {
|
||||
remoteIP := c.ClientIP()
|
||||
|
||||
if len(h.trustedProxies) > 0 {
|
||||
for _, proxy := range h.trustedProxies {
|
||||
if remoteIP == proxy {
|
||||
forwarded := c.GetHeader("X-Forwarded-For")
|
||||
if forwarded != "" {
|
||||
return strings.Split(forwarded, ",")[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return remoteIP
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func New(database *db.DB, cfg *config.Config) *Router {
|
|||
emailService := mail.NewEmailService(cfg)
|
||||
|
||||
r := &Router{
|
||||
authHandler: handlers.NewAuthHandler(database, jwtManager, emailService),
|
||||
authHandler: handlers.NewAuthHandler(database, jwtManager, emailService, cfg.TrustedProxies),
|
||||
domainHandler: handlers.NewDomainHandler(database),
|
||||
userHandler: handlers.NewUserHandler(database),
|
||||
aliasHandler: handlers.NewAliasHandler(database),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue