Skip to content

Commit 7e11d57

Browse files
authored
Merge pull request #197 from bramvbilsen/master
Add Opt-In Functionality for Hashed Session Token Storage
2 parents d7ab9d9 + 7134b6f commit 7e11d57

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

data.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scs
33
import (
44
"context"
55
"crypto/rand"
6+
"crypto/sha256"
67
"encoding/base64"
78
"fmt"
89
"sort"
@@ -623,6 +624,11 @@ func generateToken() (string, error) {
623624
return base64.RawURLEncoding.EncodeToString(b), nil
624625
}
625626

627+
func hashToken(token string) string {
628+
hash := sha256.Sum256([]byte(token))
629+
return base64.RawURLEncoding.EncodeToString(hash[:])
630+
}
631+
626632
type contextKey string
627633

628634
var (
@@ -638,6 +644,9 @@ func generateContextKey() contextKey {
638644
}
639645

640646
func (s *SessionManager) doStoreDelete(ctx context.Context, token string) (err error) {
647+
if s.HashTokenInStore {
648+
token = hashToken(token)
649+
}
641650
c, ok := s.Store.(interface {
642651
DeleteCtx(context.Context, string) error
643652
})
@@ -648,6 +657,9 @@ func (s *SessionManager) doStoreDelete(ctx context.Context, token string) (err e
648657
}
649658

650659
func (s *SessionManager) doStoreFind(ctx context.Context, token string) (b []byte, found bool, err error) {
660+
if s.HashTokenInStore {
661+
token = hashToken(token)
662+
}
651663
c, ok := s.Store.(interface {
652664
FindCtx(context.Context, string) ([]byte, bool, error)
653665
})
@@ -658,6 +670,9 @@ func (s *SessionManager) doStoreFind(ctx context.Context, token string) (b []byt
658670
}
659671

660672
func (s *SessionManager) doStoreCommit(ctx context.Context, token string, b []byte, expiry time.Time) (err error) {
673+
if s.HashTokenInStore {
674+
token = hashToken(token)
675+
}
661676
c, ok := s.Store.(interface {
662677
CommitCtx(context.Context, string, []byte, time.Time) error
663678
})

data_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,50 @@ func TestSessionManager_Load(T *testing.T) {
198198
t.Error("returned context is unexpectedly nil")
199199
}
200200
})
201+
202+
T.Run("with token hashing", func(t *testing.T) {
203+
s := New()
204+
s.HashTokenInStore = true
205+
s.IdleTimeout = time.Hour * 24
206+
207+
expectedToken := "example"
208+
expectedExpiry := time.Now().Add(time.Hour)
209+
210+
initialCtx := context.WithValue(context.Background(), s.contextKey, &sessionData{
211+
deadline: expectedExpiry,
212+
token: expectedToken,
213+
values: map[string]interface{}{
214+
"blah": "blah",
215+
},
216+
mu: sync.Mutex{},
217+
})
218+
219+
actualToken, actualExpiry, err := s.Commit(initialCtx)
220+
if expectedToken != actualToken {
221+
t.Errorf("expected token to equal %q, but received %q", expectedToken, actualToken)
222+
}
223+
if expectedExpiry != actualExpiry {
224+
t.Errorf("expected expiry to equal %v, but received %v", expectedExpiry, actualExpiry)
225+
}
226+
if err != nil {
227+
t.Errorf("unexpected error returned: %v", err)
228+
}
229+
230+
retrievedCtx, err := s.Load(context.Background(), expectedToken)
231+
if err != nil {
232+
t.Errorf("unexpected error returned: %v", err)
233+
}
234+
retrievedSessionData, ok := retrievedCtx.Value(s.contextKey).(*sessionData)
235+
if !ok {
236+
t.Errorf("unexpected data in retrieved context")
237+
} else if retrievedSessionData.token != expectedToken {
238+
t.Errorf("expected token in context's session data data to equal %v, but received %v", expectedToken, retrievedSessionData.token)
239+
}
240+
241+
if err := s.Destroy(retrievedCtx); err != nil {
242+
t.Errorf("unexpected error returned: %v", err)
243+
}
244+
})
201245
}
202246

203247
func TestSessionManager_Commit(T *testing.T) {
@@ -320,6 +364,35 @@ func TestSessionManager_Commit(T *testing.T) {
320364
t.Error("expected error not returned")
321365
}
322366
})
367+
368+
T.Run("with token hashing", func(t *testing.T) {
369+
s := New()
370+
s.HashTokenInStore = true
371+
s.IdleTimeout = time.Hour * 24
372+
373+
expectedToken := "example"
374+
expectedExpiry := time.Now().Add(time.Hour)
375+
376+
ctx := context.WithValue(context.Background(), s.contextKey, &sessionData{
377+
deadline: expectedExpiry,
378+
token: expectedToken,
379+
values: map[string]interface{}{
380+
"blah": "blah",
381+
},
382+
mu: sync.Mutex{},
383+
})
384+
385+
actualToken, actualExpiry, err := s.Commit(ctx)
386+
if expectedToken != actualToken {
387+
t.Errorf("expected token to equal %q, but received %q", expectedToken, actualToken)
388+
}
389+
if expectedExpiry != actualExpiry {
390+
t.Errorf("expected expiry to equal %v, but received %v", expectedExpiry, actualExpiry)
391+
}
392+
if err != nil {
393+
t.Errorf("unexpected error returned: %v", err)
394+
}
395+
})
323396
}
324397

325398
func TestPut(t *testing.T) {

session.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ type SessionManager struct {
4545
// a function which logs the error and returns a customized HTML error page.
4646
ErrorFunc func(http.ResponseWriter, *http.Request, error)
4747

48+
// HashTokenInStore controls whether or not to store the session token or a hashed version in the store.
49+
HashTokenInStore bool
50+
4851
// contextKey is the key used to set and retrieve the session data from a
4952
// context.Context. It's automatically generated to ensure uniqueness.
5053
contextKey contextKey

0 commit comments

Comments
 (0)