webrtc, hls: prevent brute-force attacks by waiting before sending responses (#2100)
Some checks reported warnings
lint / code (push) Has been cancelled
lint / mod-tidy (push) Has been cancelled
lint / apidocs (push) Has been cancelled
test / test64 (push) Has been cancelled
test / test32 (push) Has been cancelled
test / test_highlevel (push) Has been cancelled

This commit is contained in:
Alessandro Ros
2023-07-23 20:18:58 +02:00
committed by GitHub
parent 0137734294
commit 1fa53b49d4
6 changed files with 29 additions and 5 deletions

View File

@@ -7,6 +7,7 @@ import (
"net/http" "net/http"
gopath "path" gopath "path"
"strings" "strings"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -14,6 +15,10 @@ import (
"github.com/bluenviron/mediamtx/internal/logger" "github.com/bluenviron/mediamtx/internal/logger"
) )
const (
hlsPauseAfterAuthError = 2 * time.Second
)
//go:embed hls_index.html //go:embed hls_index.html
var hlsIndex []byte var hlsIndex []byte
@@ -166,6 +171,10 @@ func (s *hlsHTTPServer) onRequest(ctx *gin.Context) {
remoteAddr := net.JoinHostPort(ip, port) remoteAddr := net.JoinHostPort(ip, port)
s.Log(logger.Info, "connection %v failed to authenticate: %v", remoteAddr, terr.message) s.Log(logger.Info, "connection %v failed to authenticate: %v", remoteAddr, terr.message)
// wait some seconds to stop brute force attacks
<-time.After(hlsPauseAfterAuthError)
ctx.Writer.WriteHeader(http.StatusUnauthorized) ctx.Writer.WriteHeader(http.StatusUnauthorized)
return return
} }

View File

@@ -30,7 +30,7 @@ import (
) )
const ( const (
rtmpConnPauseAfterAuthError = 2 * time.Second rtmpPauseAfterAuthError = 2 * time.Second
) )
func pathNameAndQuery(inURL *url.URL) (string, url.Values, string) { func pathNameAndQuery(inURL *url.URL) (string, url.Values, string) {
@@ -367,7 +367,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
if res.err != nil { if res.err != nil {
if terr, ok := res.err.(*errAuthentication); ok { if terr, ok := res.err.(*errAuthentication); ok {
// wait some seconds to stop brute force attacks // wait some seconds to stop brute force attacks
<-time.After(rtmpConnPauseAfterAuthError) <-time.After(rtmpPauseAfterAuthError)
return terr return terr
} }
return res.err return res.err
@@ -782,7 +782,7 @@ func (c *rtmpConn) runPublish(u *url.URL) error {
if res.err != nil { if res.err != nil {
if terr, ok := res.err.(*errAuthentication); ok { if terr, ok := res.err.(*errAuthentication); ok {
// wait some seconds to stop brute force attacks // wait some seconds to stop brute force attacks
<-time.After(rtmpConnPauseAfterAuthError) <-time.After(rtmpPauseAfterAuthError)
return terr return terr
} }
return res.err return res.err

View File

@@ -17,7 +17,7 @@ import (
) )
const ( const (
rtspConnPauseAfterAuthError = 2 * time.Second rtspPauseAfterAuthError = 2 * time.Second
) )
type rtspConnParent interface { type rtspConnParent interface {
@@ -204,7 +204,7 @@ func (c *rtspConn) handleAuthError(authErr error) (*base.Response, error) {
} }
// wait some seconds to stop brute force attacks // wait some seconds to stop brute force attacks
<-time.After(rtspConnPauseAfterAuthError) <-time.After(rtspPauseAfterAuthError)
return &base.Response{ return &base.Response{
StatusCode: base.StatusUnauthorized, StatusCode: base.StatusUnauthorized,

View File

@@ -10,6 +10,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
@@ -318,6 +319,10 @@ func (s *webRTCHTTPServer) onRequest(ctx *gin.Context) {
} }
s.Log(logger.Info, "connection %v failed to authenticate: %v", remoteAddr, terr.message) s.Log(logger.Info, "connection %v failed to authenticate: %v", remoteAddr, terr.message)
// wait some seconds to stop brute force attacks
<-time.After(webrtcPauseAfterAuthError)
ctx.Writer.WriteHeader(http.StatusUnauthorized) ctx.Writer.WriteHeader(http.StatusUnauthorized)
return return
} }

View File

@@ -23,6 +23,7 @@ import (
) )
const ( const (
webrtcPauseAfterAuthError = 2 * time.Second
webrtcHandshakeTimeout = 10 * time.Second webrtcHandshakeTimeout = 10 * time.Second
webrtcTrackGatherTimeout = 2 * time.Second webrtcTrackGatherTimeout = 2 * time.Second
webrtcPayloadMaxSize = 1188 // 1200 - 12 (RTP header) webrtcPayloadMaxSize = 1188 // 1200 - 12 (RTP header)

View File

@@ -240,8 +240,12 @@ func (s *webRTCSession) runPublish() (int, error) {
}) })
if res.err != nil { if res.err != nil {
if _, ok := res.err.(*errAuthentication); ok { if _, ok := res.err.(*errAuthentication); ok {
// wait some seconds to stop brute force attacks
<-time.After(webrtcPauseAfterAuthError)
return http.StatusUnauthorized, res.err return http.StatusUnauthorized, res.err
} }
return http.StatusBadRequest, res.err return http.StatusBadRequest, res.err
} }
@@ -363,11 +367,16 @@ func (s *webRTCSession) runRead() (int, error) {
}) })
if res.err != nil { if res.err != nil {
if _, ok := res.err.(*errAuthentication); ok { if _, ok := res.err.(*errAuthentication); ok {
// wait some seconds to stop brute force attacks
<-time.After(webrtcPauseAfterAuthError)
return http.StatusUnauthorized, res.err return http.StatusUnauthorized, res.err
} }
if strings.HasPrefix(res.err.Error(), "no one is publishing") { if strings.HasPrefix(res.err.Error(), "no one is publishing") {
return http.StatusNotFound, res.err return http.StatusNotFound, res.err
} }
return http.StatusBadRequest, res.err return http.StatusBadRequest, res.err
} }