From 6d6ebee80dd6ee1955f2f156ed8df7d0b0885e0d Mon Sep 17 00:00:00 2001 From: Alessandro Ros Date: Mon, 6 Apr 2026 18:03:22 +0200 Subject: [PATCH] deprecate authJWTInHTTPQuery and disable JWTs in query parameters (#5648) This fixes a long standing security flaw. Even though it's a breaking change, few users should be impacted since this feature has been discouraged for some time. --- api/openapi.yaml | 6 ++++-- internal/auth/manager.go | 7 ++++--- internal/conf/conf.go | 7 +++++-- internal/core/core.go | 2 +- mediamtx.yml | 4 ---- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/api/openapi.yaml b/api/openapi.yaml index 65f455cd..afffcb31 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -270,6 +270,10 @@ components: type: string authJWTClaimKey: type: string + authJWTInHTTPQuery: + type: boolean + nullable: true + deprecated: true authJWTIssuer: type: string authJWTAudience: @@ -278,8 +282,6 @@ components: type: array items: $ref: '#/components/schemas/AuthInternalUserPermission' - authJWTInHTTPQuery: - type: boolean # Control API api: diff --git a/internal/auth/manager.go b/internal/auth/manager.go index f2c2bdac..bb7557a1 100644 --- a/internal/auth/manager.go +++ b/internal/auth/manager.go @@ -63,7 +63,7 @@ func matchesPermission(perms []conf.AuthInternalUserPermission, req *Request) bo return false } -func getToken(jwtInHTTPQuery bool, req *Request) (string, bool) { +func getToken(jwtInHTTPQuery *bool, req *Request) (string, bool) { switch { case req.Credentials.Token != "": return req.Credentials.Token, true @@ -72,7 +72,8 @@ func getToken(jwtInHTTPQuery bool, req *Request) (string, bool) { return req.Credentials.Pass, true // always allow passing tokens through query parameters with RTSP and RTMP since there's no alternative. - case req.Protocol == ProtocolRTSP || req.Protocol == ProtocolRTMP || (jwtInHTTPQuery && isHTTP(req)): + case req.Protocol == ProtocolRTSP || req.Protocol == ProtocolRTMP || + (jwtInHTTPQuery != nil && *jwtInHTTPQuery && isHTTP(req)): v, err := url.ParseQuery(req.Query) if err == nil { if len(v["token"]) == 1 { @@ -100,7 +101,7 @@ type Manager struct { JWTJWKSFingerprint string JWTClaimKey string JWTExclude []conf.AuthInternalUserPermission - JWTInHTTPQuery bool + JWTInHTTPQuery *bool JWTIssuer string JWTAudience string ReadTimeout time.Duration diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 1188972f..9148bbe9 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -268,7 +268,7 @@ type Conf struct { AuthJWTJWKSFingerprint string `json:"authJWTJWKSFingerprint"` AuthJWTClaimKey string `json:"authJWTClaimKey"` AuthJWTExclude []AuthInternalUserPermission `json:"authJWTExclude"` - AuthJWTInHTTPQuery bool `json:"authJWTInHTTPQuery"` + AuthJWTInHTTPQuery *bool `json:"authJWTInHTTPQuery,omitempty" deprecated:"true"` AuthJWTIssuer string `json:"authJWTIssuer"` AuthJWTAudience string `json:"authJWTAudience"` @@ -437,7 +437,6 @@ func (conf *Conf) setDefaults() { }, } conf.AuthJWTClaimKey = "mediamtx_permissions" - conf.AuthJWTInHTTPQuery = true // Control API conf.APIAddress = ":9997" @@ -727,6 +726,10 @@ func (conf *Conf) Validate(l logger.Writer) error { } } + if conf.AuthJWTInHTTPQuery != nil { + l.Log(logger.Warn, "parameter 'authJWTInHTTPQuery' is deprecated and will be removed in a future release") + } + // Control API (deprecated params) if conf.APIAllowOrigin != nil { diff --git a/internal/core/core.go b/internal/core/core.go index ef272c79..d909886e 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -739,7 +739,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) { newConf.AuthJWTJWKSFingerprint != p.conf.AuthJWTJWKSFingerprint || newConf.AuthJWTClaimKey != p.conf.AuthJWTClaimKey || !reflect.DeepEqual(newConf.AuthJWTExclude, p.conf.AuthJWTExclude) || - newConf.AuthJWTInHTTPQuery != p.conf.AuthJWTInHTTPQuery || + !reflect.DeepEqual(newConf.AuthJWTInHTTPQuery, p.conf.AuthJWTInHTTPQuery) || newConf.AuthJWTIssuer != p.conf.AuthJWTIssuer || newConf.AuthJWTAudience != p.conf.AuthJWTAudience || newConf.ReadTimeout != p.conf.ReadTimeout diff --git a/mediamtx.yml b/mediamtx.yml index 9a164ba0..881114a3 100644 --- a/mediamtx.yml +++ b/mediamtx.yml @@ -149,10 +149,6 @@ authJWTClaimKey: mediamtx_permissions # Actions to exclude from JWT-based authentication. # Format is the same as the one of user permissions. authJWTExclude: [] -# Allow passing the JWT through query parameters of HTTP requests (i.e. ?token=JWT). -# This is a security risk and will be disabled in the future. -# RTSP and RTMP always allow JWT in query even if disabled, since there is no alternative. -authJWTInHTTPQuery: true # Expected issuer (iss) claim in the JWT. Leave empty to skip validation. authJWTIssuer: # Expected audience (aud) claim in the JWT. Leave empty to skip validation.