Golang中密码错误返回的两种情况原因分析

Golang中密码错误返回的两种情况原因分析 为什么无论用户输入错误密码还是正确密码,都会返回错误?

另外,如何向用户显示他们的邮箱也是错误的?

func Login(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var user model.User
	var dbUser model.User
	if err := json.NewDecoder(r.Body).Decode(&user); err == nil {
		if _, err := service.Login_User(&user); err == nil {
			json.NewEncoder(w).Encode(user)
		} else {
			w.WriteHeader(http.StatusInternalServerError)
			//w.Write([]byte)
			json.NewEncoder(w).Encode(err)
		}
	} else {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	userPass := []byte(user.Password)
	dbPass := []byte(dbUser.Password)
	PassErr := bcrypt.CompareHashAndPassword(dbPass, userPass)
	if PassErr != nil {
		w.Write([]byte(`{"response":"Wrong Password"}`))
		return
	}
}

更多关于Golang中密码错误返回的两种情况原因分析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我也遇到了同样的错误,对此有什么建议吗?请回复。先谢谢了。

更多关于Golang中密码错误返回的两种情况原因分析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好,@Rajrup_Das

这个函数似乎没有在任何地方填充 dbUser

关于电子邮件,我猜 model.User 中有一个字段,你需要通过检查电子邮件字符串是否包含“@”并且包含电子邮件地址中非法的字符来验证它。应该有一些可用的包来处理这个。(搜索 pkg.go.dev,我相信你会找到不止一个。)

你的代码存在几个关键问题,导致无论密码是否正确都返回错误:

主要问题分析:

1. 逻辑顺序错误

密码验证代码被放在了错误处理块之后,导致永远不会执行。

2. 数据库查询缺失

dbUser 变量从未从数据库获取数据,始终为空。

3. 错误处理逻辑混乱

service.Login_User(&user) 成功后直接返回,但后续还有密码验证代码。

修正后的代码:

func Login(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    
    var loginRequest struct {
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    
    // 解析请求体
    if err := json.NewDecoder(r.Body).Decode(&loginRequest); err != nil {
        http.Error(w, "Invalid request format", http.StatusBadRequest)
        return
    }
    
    // 1. 先验证邮箱是否存在
    dbUser, err := service.GetUserByEmail(loginRequest.Email)
    if err != nil {
        // 邮箱不存在的情况
        w.WriteHeader(http.StatusUnauthorized)
        json.NewEncoder(w).Encode(map[string]string{
            "error": "Invalid email or password", // 不明确提示邮箱错误
        })
        return
    }
    
    // 2. 验证密码
    userPass := []byte(loginRequest.Password)
    dbPass := []byte(dbUser.Password)
    
    if err := bcrypt.CompareHashAndPassword(dbPass, userPass); err != nil {
        // 密码错误的情况
        w.WriteHeader(http.StatusUnauthorized)
        json.NewEncoder(w).Encode(map[string]string{
            "error": "Invalid email or password", // 保持相同错误信息
        })
        return
    }
    
    // 3. 登录成功
    json.NewEncoder(w).Encode(map[string]interface{}{
        "message": "Login successful",
        "user":    dbUser,
    })
}

安全建议的实现:

如果需要区分错误类型但保持安全,可以这样处理:

func Login(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    
    var loginRequest struct {
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    
    if err := json.NewDecoder(r.Body).Decode(&loginRequest); err != nil {
        http.Error(w, "Invalid request format", http.StatusBadRequest)
        return
    }
    
    // 验证邮箱格式
    if !isValidEmail(loginRequest.Email) {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(map[string]string{
            "error": "Invalid email format",
        })
        return
    }
    
    // 检查用户是否存在
    userExists, err := service.CheckUserExists(loginRequest.Email)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    
    if !userExists {
        // 模拟密码验证耗时,防止邮箱枚举攻击
        bcrypt.CompareHashAndPassword(
            []byte("$2a$10$fakehashforsecurity"),
            []byte(loginRequest.Password),
        )
        
        w.WriteHeader(http.StatusUnauthorized)
        json.NewEncoder(w).Encode(map[string]string{
            "error": "Invalid credentials",
        })
        return
    }
    
    // 获取用户并验证密码
    dbUser, err := service.GetUserByEmail(loginRequest.Email)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    
    if err := bcrypt.CompareHashAndPassword(
        []byte(dbUser.Password),
        []byte(loginRequest.Password),
    ); err != nil {
        w.WriteHeader(http.StatusUnauthorized)
        json.NewEncoder(w).Encode(map[string]string{
            "error": "Invalid credentials",
        })
        return
    }
    
    // 登录成功
    json.NewEncoder(w).Encode(map[string]interface{}{
        "message": "Login successful",
        "user":    sanitizeUserData(dbUser),
    })
}

// 辅助函数:清理用户敏感数据
func sanitizeUserData(user model.User) map[string]interface{} {
    return map[string]interface{}{
        "id":    user.ID,
        "email": user.Email,
        "name":  user.Name,
    }
}

// 辅助函数:验证邮箱格式
func isValidEmail(email string) bool {
    return strings.Contains(email, "@") && strings.Contains(email, ".")
}

关键改进点:

  1. 统一错误信息:始终返回"Invalid credentials",防止攻击者枚举有效邮箱
  2. 时序一致性:邮箱不存在时也进行密码验证,保持响应时间一致
  3. 逻辑清晰:按顺序验证邮箱格式→邮箱存在→密码正确
  4. 数据清理:返回用户数据时移除密码等敏感信息

这样的实现既保证了安全性,又修复了原代码中的逻辑错误。

回到顶部