Browse Source

Reverse data in session ids.

The session ids contain a Gorilla SecureCookie which internally encodes
"timestamp|value". As a result, newer session ids compare "greater"
than older ids. This is a problem for conferences where the participants
decide based on comparison of the session id who calls who, so later
participants always have to call all of the existing participants.

With this change, the (random) value is at the start of the session id,
resulting in "random" ordering of participants. Also some tests have
been added to check session en-/decoding.
pull/285/head
Joachim Bauch 9 years ago
parent
commit
c46c0b7f14
  1. 49
      go/channelling/tickets.go
  2. 97
      go/channelling/tickets_test.go

49
go/channelling/tickets.go

@ -34,6 +34,11 @@ import (
"github.com/gorilla/securecookie" "github.com/gorilla/securecookie"
) )
var (
// Can be set from tests to disable some log outputs.
silentOutput = false
)
type SessionValidator interface { type SessionValidator interface {
Realm() string Realm() string
ValidateSession(string, string) bool ValidateSession(string, string) bool
@ -77,6 +82,27 @@ func (tickets *tickets) Realm() string {
return tickets.realm return tickets.realm
} }
func reverseBase64String(s string) (string, error) {
decoded, err := base64.URLEncoding.DecodeString(s)
if err != nil {
return "", err
}
for i, j := 0, len(decoded)-1; i < j; i, j = i+1, j-1 {
decoded[i], decoded[j] = decoded[j], decoded[i]
}
return base64.URLEncoding.EncodeToString(decoded), nil
}
func (tickets *tickets) reverseSessionId(id string) string {
reversedId, err := reverseBase64String(id)
if err != nil {
// This should never happen
panic("Could not reverse " + id)
}
return reversedId
}
func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) { func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) {
var err error var err error
if token != "" { if token != "" {
@ -90,8 +116,11 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) {
if st == nil || err != nil { if st == nil || err != nil {
sid := randomstring.NewRandomString(32) sid := randomstring.NewRandomString(32)
id, _ := tickets.Encode("id", sid) id, _ := tickets.Encode("id", sid)
id = tickets.reverseSessionId(id)
st = &SessionToken{Id: id, Sid: sid} st = &SessionToken{Id: id, Sid: sid}
log.Println("Created new session id", id) if !silentOutput {
log.Println("Created new session id", id)
}
} }
return return
} }
@ -99,6 +128,7 @@ func (tickets *tickets) DecodeSessionToken(token string) (st *SessionToken) {
func (tickets *tickets) FakeSessionToken(userid string) (st *SessionToken) { func (tickets *tickets) FakeSessionToken(userid string) (st *SessionToken) {
sid := fmt.Sprintf("fake-%s", randomstring.NewRandomString(27)) sid := fmt.Sprintf("fake-%s", randomstring.NewRandomString(27))
id, _ := tickets.Encode("id", sid) id, _ := tickets.Encode("id", sid)
id = tickets.reverseSessionId(id)
st = &SessionToken{Id: id, Sid: sid, Userid: userid} st = &SessionToken{Id: id, Sid: sid, Userid: userid}
log.Println("Created new fake session id", st.Id) log.Println("Created new fake session id", st.Id)
return return
@ -106,12 +136,23 @@ func (tickets *tickets) FakeSessionToken(userid string) (st *SessionToken) {
func (tickets *tickets) ValidateSession(id, sid string) bool { func (tickets *tickets) ValidateSession(id, sid string) bool {
var decoded string var decoded string
if err := tickets.Decode("id", id, &decoded); err != nil { reversedId, err := reverseBase64String(id)
log.Println("Session validation error", err, id, sid) if err != nil {
if !silentOutput {
log.Println("Session format error", err, id, sid)
}
return false
}
if err := tickets.Decode("id", reversedId, &decoded); err != nil {
if !silentOutput {
log.Println("Session validation error", err, reversedId, sid)
}
return false return false
} }
if decoded != sid { if decoded != sid {
log.Println("Session validation failed", id, sid) if !silentOutput {
log.Println("Session validation failed", reversedId, sid)
}
return false return false
} }
return true return true

97
go/channelling/tickets_test.go

@ -0,0 +1,97 @@
/*
* Spreed WebRTC.
* Copyright (C) 2016 struktur AG
*
* This file is part of Spreed WebRTC.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package channelling
import (
"crypto/rand"
"encoding/base64"
"testing"
)
func getRandom(n int) ([]byte, error) {
result := make([]byte, n)
if _, err := rand.Read(result); err != nil {
return nil, err
}
return result, nil
}
func Test_ReverseBase64(t *testing.T) {
for i := 0; i < 1000; i++ {
data, err := getRandom(64)
if err != nil {
t.Errorf("Could not get random data: %v", err)
continue
}
s := base64.URLEncoding.EncodeToString(data)
reversed, err := reverseBase64String(s)
if err != nil {
t.Errorf("Could not reverse %s: %v", s, err)
continue
}
if s == reversed {
t.Errorf("Reversing should be different for %s", s)
}
original, err := reverseBase64String(reversed)
if err != nil {
t.Errorf("Could not reverse back %s: %v", reversed, err)
continue
}
if s != original {
t.Errorf("Reversing back should have restored %s from %s but got %s", s, reversed, original)
}
}
}
func Test_Sessions(t *testing.T) {
sessionSecret, err := getRandom(64)
if err != nil {
t.Fatalf("Could not create session secret: %v", err)
return
}
encryptionSecret, err := getRandom(32)
if err != nil {
t.Fatalf("Could not create encryption secret: %v", err)
return
}
tickets := NewTickets(sessionSecret, encryptionSecret, "test")
silentOutput = true
for i := 0; i < 1000; i++ {
st := tickets.DecodeSessionToken("")
if st == nil {
t.Error("Could not create session")
continue
}
if !tickets.ValidateSession(st.Id, st.Sid) {
t.Errorf("Session is invalid: %v", st)
continue
}
}
silentOutput = false
}
Loading…
Cancel
Save