From 65fa0be8fef83662ffdb52c209fd92606393c926 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Thu, 11 Jan 2024 21:13:25 +0100 Subject: [PATCH] Obfuscate WHIP ids. If the WHIP session is not authenticated, then the only thing preventing an attacker from DELETEing the session is the session URL. Since client ids are known, obfuscate the id before using it in the session URL. --- webserver/webserver_test.go | 20 +++++++++++ webserver/whip.go | 68 ++++++++++++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/webserver/webserver_test.go b/webserver/webserver_test.go index faf4a5a3..e9186206 100644 --- a/webserver/webserver_test.go +++ b/webserver/webserver_test.go @@ -136,3 +136,23 @@ func TestFormatICEServer(t *testing.T) { }) } } + +func TestObfuscate(t *testing.T) { + id := newId() + obfuscated, err := obfuscate(id) + if err != nil { + t.Fatalf("obfuscate: %v", err) + } + id2, err := deobfuscate(obfuscated) + if err != nil { + t.Fatalf("deobfuscate: %v", err) + } + if id != id2 { + t.Errorf("not equal: %v, %v", id, id2) + } + + _, err = obfuscate("toto") + if err == nil { + t.Errorf("obfuscate: no errror") + } +} diff --git a/webserver/whip.go b/webserver/whip.go index d240022e..e98063f4 100644 --- a/webserver/whip.go +++ b/webserver/whip.go @@ -2,8 +2,11 @@ package webserver import ( "bytes" + "crypto/aes" + "crypto/cipher" crand "crypto/rand" "encoding/base64" + "errors" "fmt" "io" "log" @@ -37,12 +40,56 @@ func parseWhip(pth string) (string, string) { return "", "" } +var idSecret []byte +var idCipher cipher.Block + +func init() { + idSecret = make([]byte, 16) + crand.Read(idSecret) + var err error + idCipher, err = aes.NewCipher(idSecret) + if err != nil { + log.Fatalf("NewCipher: %v", err) + } +} + func newId() string { - b := make([]byte, 16) + b := make([]byte, idCipher.BlockSize()) crand.Read(b) return base64.RawURLEncoding.EncodeToString(b) } +// we obfuscate ids to avoid exposing the WHIP session URL +func obfuscate(id string) (string, error) { + v, err := base64.RawURLEncoding.DecodeString(id) + if err != nil { + return "", err + } + + if len(v) != idCipher.BlockSize() { + return "", errors.New("bad length") + } + + idCipher.Encrypt(v, v) + + return base64.RawURLEncoding.EncodeToString(v), nil +} + +func deobfuscate(id string) (string, error) { + v, err := base64.RawURLEncoding.DecodeString(id) + if err != nil { + return "", err + } + + if len(v) != idCipher.BlockSize() { + return "", errors.New("bad length") + } + + idCipher.Decrypt(v, v) + + return base64.RawURLEncoding.EncodeToString(v), nil +} + func canPresent(perms []string) bool { for _, p := range perms { if p == "present" { @@ -186,6 +233,13 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) { } id := newId() + obfuscated, err := obfuscate(id) + if err != nil { + http.Error(w, "Internal Server Error", + http.StatusInternalServerError) + return + } + c := rtpconn.NewWhipClient(g, id, token) _, err = group.AddClient(g.Name(), c, creds) @@ -214,7 +268,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) { http.StatusInternalServerError) } - w.Header().Set("Location", path.Join(r.URL.Path, id)) + w.Header().Set("Location", path.Join(r.URL.Path, obfuscated)) w.Header().Set("Access-Control-Expose-Headers", "Location, Content-Type, Link") whipICEServers(w) @@ -226,8 +280,14 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) { } func whipResourceHandler(w http.ResponseWriter, r *http.Request) { - pth, id := parseWhip(r.URL.Path) - if pth == "" || id == "" { + pth, obfuscated := parseWhip(r.URL.Path) + if pth == "" || obfuscated == "" { + http.Error(w, "Internal server error", + http.StatusInternalServerError) + return + } + id, err := deobfuscate(obfuscated) + if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return