perf: sync protocol and core hardening updates

This commit is contained in:
A 2026-07-11 19:48:26 +08:00
parent 152fed3b87
commit 4390ebf5a9
283 changed files with 29231 additions and 2295 deletions

View file

@ -2,14 +2,24 @@ package rpc
import (
"context"
"crypto/sha256"
"encoding/hex"
"strconv"
"time"
"go.uber.org/zap"
"telesrv/internal/domain"
)
const sendRateLimitKeyPrefix = "messages:send:"
const (
authCodePhoneRateLimitKeyPrefix = "auth:code:phone-sha256:"
authCodeAuthKeyRateLimitKeyPrefix = "auth:code:raw-auth-key:"
defaultAuthCodeRateWindow = 10 * time.Minute
)
const (
channelDifferenceRateLimitKeyPrefix = "updates:channeldifference:"
peerDialogsRateLimitKeyPrefix = "messages:peerdialogs:"
@ -65,3 +75,55 @@ func (r *Router) checkSendRateLimit(ctx context.Context, userID int64, cost int)
r.metrics().MessageRateLimited(retryAfter)
return floodWaitErr(retryAfter)
}
// checkAuthCodeRateLimit protects the unauthenticated code-issuance path before
// any account lookup or durable 777000 write. Existing and unknown phone numbers
// therefore consume identical budgets and cannot be distinguished through the
// limiter. Plaintext phone numbers are never used as limiter keys or log fields.
func (r *Router) checkAuthCodeRateLimit(ctx context.Context, phone string) error {
if r.deps.Limiter == nil {
return nil
}
normalizedPhone := domain.NormalizePhone(phone)
if !domain.ValidPhone(normalizedPhone) {
return phoneNumberInvalidErr()
}
window := r.cfg.AuthCodeRateWindow
if window <= 0 {
window = defaultAuthCodeRateWindow
}
// Check the connection/auth-key budget first. If that dimension is already
// blocked, changing phone strings cannot create one phone-digest Redis key
// per attempt and bypass the intended cardinality bound.
if limit := r.cfg.AuthCodeAuthKeyRateLimit; limit > 0 {
if rawAuthKeyID, ok := RawAuthKeyIDFrom(ctx); ok && rawAuthKeyID != ([8]byte{}) {
if err := r.checkAuthCodeRateLimitKey(ctx, authCodeAuthKeyRateLimitKeyPrefix+hex.EncodeToString(rawAuthKeyID[:]), limit, window, "raw_auth_key"); err != nil {
return err
}
}
}
if limit := r.cfg.AuthCodePhoneRateLimit; limit > 0 {
digest := sha256.Sum256([]byte(normalizedPhone))
if err := r.checkAuthCodeRateLimitKey(ctx, authCodePhoneRateLimitKeyPrefix+hex.EncodeToString(digest[:]), limit, window, "phone_digest"); err != nil {
return err
}
}
return nil
}
func (r *Router) checkAuthCodeRateLimitKey(ctx context.Context, key string, limit int, window time.Duration, dimension string) error {
allowed, retryAfter, err := r.deps.Limiter.AllowN(ctx, key, 1, limit, window)
if err != nil {
return internalErr()
}
if allowed {
return nil
}
if retryAfter <= 0 {
retryAfter = 1
}
r.log.Debug("auth code issuance rate limited",
zap.String("dimension", dimension),
zap.Int("retry_after", retryAfter))
return floodWaitErr(retryAfter)
}