added extra IP checks for the connected realtime client

This commit is contained in:
Gani Georgiev
2026-05-18 19:13:25 +03:00
parent f7fbc6c2c3
commit b9b0e5ae80
5 changed files with 96 additions and 7 deletions
+24 -3
View File
@@ -28,6 +28,9 @@ const clientsChunkSize = 150
// RealtimeClientAuthKey is the name of the realtime client store key that holds its auth state.
const RealtimeClientAuthKey = "auth"
// RealtimeClientIPKey is the name of the realtime client store key that holds the IP of the connected client.
const RealtimeClientIPKey = "pbRealtimeClientIP"
// bindRealtimeApi registers the realtime api endpoints.
func bindRealtimeApi(app core.App, rg *router.RouterGroup[*core.RequestEvent]) {
sub := rg.Group("/realtime")
@@ -63,9 +66,12 @@ func realtimeConnect(e *core.RequestEvent) error {
connectEvent := new(core.RealtimeConnectRequestEvent)
connectEvent.RequestEvent = e
connectEvent.Client = subscriptions.NewDefaultClient()
connectEvent.IdleTimeout = 5 * time.Minute
connectEvent.MaxTimeout = 30 * time.Minute
connectEvent.Client = subscriptions.NewDefaultClient()
// could be used as an optional cross-reference check in other API endpoints
connectEvent.Client.Set(RealtimeClientIPKey, e.RealIP())
return e.App.OnRealtimeConnectRequest().Trigger(connectEvent, func(ce *core.RealtimeConnectRequestEvent) error {
// register new subscription client
@@ -74,7 +80,7 @@ func realtimeConnect(e *core.RequestEvent) error {
e.App.SubscriptionsBroker().Unregister(ce.Client.Id())
}()
ce.App.Logger().Debug("Realtime connection established.", slog.String("clientId", ce.Client.Id()))
ce.App.Logger().Debug("Realtime connection established", slog.String("clientId", ce.Client.Id()))
// signalize established connection (aka. fire "connect" message)
connectMsgEvent := new(core.RealtimeMessageEvent)
@@ -194,6 +200,21 @@ func realtimeSetSubscriptions(e *core.RequestEvent) error {
return e.NotFoundError("Missing or invalid client id.", err)
}
// for just in case to prevent someone changing a guest subscription
//
// note1: this is an extra precaution against clientId bruteforce attempts
// for installations allowing longer realtime connections duration
//
// note2: custom registered clients (aka. those without IP in the store)
// are excluded from the check for backward compatibility
clientIP, _ := client.Get(RealtimeClientIPKey).(string)
if clientIP != "" && clientIP != e.RealIP() {
return e.BadRequestError(
"Invalid realtime client.",
errors.New("the subscription request IP doesn't match with the realtime client IP"),
)
}
// for now allow only guest->auth upgrades and any other auth change is forbidden
clientAuth, _ := client.Get(RealtimeClientAuthKey).(*core.Record)
if clientAuth != nil && !isSameAuth(clientAuth, e.Auth) {
@@ -216,7 +237,7 @@ func realtimeSetSubscriptions(e *core.RequestEvent) error {
e.Client.Subscribe(e.Subscriptions...)
e.App.Logger().Debug(
"Realtime subscriptions updated.",
"Realtime subscriptions updated",
slog.String("clientId", e.Client.Id()),
slog.Any("subscriptions", e.Subscriptions),
)