From 03038eaf45e9b80818eec4a467fadc4312449565 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 | 70 ++++++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/webserver/webserver_test.go b/webserver/webserver_test.go index faf4a5a..e918620 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 d240022..92d4638 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,58 @@ func parseWhip(pth string) (string, string) { return "", "" } +var idSecret []byte +var idCipher cipher.Block + +func init() { + idSecret = make([]byte, 16) + _, err := crand.Read(idSecret) + if err != nil { + log.Fatalf("crand.Read: %v", err) + } + 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 +235,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 +270,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 +282,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