Go:Add build pipeline for go server and fix web login bug
This commit is contained in:
@@ -7,8 +7,25 @@ import (
|
||||
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/hub"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/protocol"
|
||||
"github.com/yuanyuanxiang/SimpleRemoter/server/go/wsauth"
|
||||
)
|
||||
|
||||
// rotateChallenge replaces the client's nonce with a fresh one and pushes a
|
||||
// {cmd:"challenge"} message so the browser updates its stored nonce. Called
|
||||
// after every login failure so a retry on the SAME WebSocket works without
|
||||
// the user having to refresh — the old nonce is still burned for replay
|
||||
// protection, but the client now has a usable new one.
|
||||
func (h *wsHub) rotateChallenge(c *wsClient) {
|
||||
n, err := wsauth.NewNonce()
|
||||
if err != nil {
|
||||
h.log.Error("nonce regen failed: %v", err)
|
||||
c.nonce = ""
|
||||
return
|
||||
}
|
||||
c.nonce = n
|
||||
c.queue([]byte(`{"cmd":"challenge","nonce":"` + n + `"}`))
|
||||
}
|
||||
|
||||
// dispatch routes one inbound message to its handler. The `raw` payload is
|
||||
// passed through so handlers can re-parse to their own shape.
|
||||
//
|
||||
@@ -125,10 +142,12 @@ func (h *wsHub) handleLogin(c *wsClient, raw []byte) {
|
||||
// with a uniform "credentials" error so the limit is not detectable.
|
||||
if !h.allowLoginByIP(c) || !h.allowLoginByUsername(in.Username) {
|
||||
h.log.Warn("ws login throttled: user=%s addr=%s", in.Username, c.addr)
|
||||
// Burn the challenge so the attacker can't immediately replay.
|
||||
c.nonce = ""
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
c.queue(mustJSON(map[string]any{"cmd": "login_result", "ok": false, "msg": "Invalid credentials"}))
|
||||
// Rotate the challenge: burns the previous nonce (replay protection)
|
||||
// AND hands the client a fresh one so the next attempt does not
|
||||
// require a page refresh.
|
||||
h.rotateChallenge(c)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -136,18 +155,18 @@ func (h *wsHub) handleLogin(c *wsClient, raw []byte) {
|
||||
// replays from a different connection can't reuse a captured response.
|
||||
if in.Nonce == "" || in.Nonce != c.nonce {
|
||||
c.queue(mustJSON(map[string]any{"cmd": "login_result", "ok": false, "msg": "Invalid challenge"}))
|
||||
h.rotateChallenge(c)
|
||||
return
|
||||
}
|
||||
|
||||
token, role, err := h.auth.VerifyLogin(in.Username, in.Response, in.Nonce)
|
||||
if err != nil {
|
||||
// Burn the challenge on failure too — forces a new round on retry.
|
||||
c.nonce = ""
|
||||
// Fixed delay on failure: makes online brute force impractical
|
||||
// even within the rate-limit budget, and erases the timing
|
||||
// difference between "wrong password" and "wrong nonce".
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
c.queue(mustJSON(map[string]any{"cmd": "login_result", "ok": false, "msg": "Invalid credentials"}))
|
||||
h.rotateChallenge(c)
|
||||
return
|
||||
}
|
||||
// Successful login: clear the per-IP/per-user budgets so a legitimate
|
||||
|
||||
Reference in New Issue
Block a user