perf: sync protocol and core hardening updates
This commit is contained in:
parent
152fed3b87
commit
4390ebf5a9
283 changed files with 29231 additions and 2295 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue