A Tale of Two Sessions
Alan Braithwaite
@Caust1c
Segment
Agenda
- What is a session anyway?
- How do they work?
- Types of Sessions
- Usage Comparison
- Tour of Jeff
What is a session?
A web session is a sequence of network HTTP request and response transactions associated to the same user.
What is a Cookie?
Attributes
Name, Value
Domain, Path
Expires
Secure
HttpOnly
SameSite
How do cookies work?
Server Side Stateful Session
Encrypted Cookie Session
Threat Model Comparison
Encrypted cookies are more secure because they're encrypted
Encrypted cookies are more secure because they're encrypted
An attacker stealing an encrypted cookie still allows them to impersonate a user.
Side note: Always use TLS/HTTPS!
Threat Model Comparison
Secrets Management
Serverside
Encrypted Cookie
None
Databases firewalled
Ultra-secret key
If compromised, attacker can impersonate any user
Threat Model Comparison
Revocation
Serverside
Encrypted Cookie
Delete key in database
???
Choose your own adventure
Will likely end up implementing server-side verification for every request
Encrypted Cookies Benefits
Server to server, request scoped Authentication
"Lazy" authentication as redundant backup
Federated Authentication with JWT
Jeff
A tool for managing Sessions
Jeff
A tool for managing Sessions
p := &redis.Pool{
MaxIdle: 3,
IdleTimeout: 240 * time.Second,
Dial: func() (redis.Conn, error) {
return redis.Dial("tcp", "localhost:6379")
},
}
str := redis_store.New(p)
s := &server{
jeff: jeff.New(str, jeff.Insecure),
}
router.HandleFunc("/", s.indexPageHandler)
router.HandleFunc("/register", s.registerFormHandler).Methods("GET")
router.HandleFunc("/register", s.registerHandler).Methods("POST")
router.HandleFunc("/login", s.loginHandler).Methods("POST")
router.Handle("/logout", s.jeff.WrapFunc(s.logoutHandler)).Methods("POST")
router.Handle("/internal", s.jeff.WrapFunc(s.internalPageHandler))
router.Handle("/public", s.jeff.PublicFunc(s.publicPageHandler))
http.Handle("/", router)
http.ListenAndServe(":8000", nil)
Jeff
A tool for managing Sessions
func (s *server) loginHandler(w http.ResponseWriter, r *http.Request) {
name := r.FormValue("name")
pass := r.FormValue("password")
user, err := authUser(name, pass)
if err != nil {
w.WriteHeader(400)
return
}
err := s.jeff.Set(r.Context(), w, []byte(user.Email))
if err != nil {
w.WriteHeader(500)
panic(err)
}
http.Redirect(w, r, "/internal", 302)
}
Jeff
A tool for managing Sessions
// This page won't be reached without an active session if wrapped
// with jeff.Wrap
func (s *server) internalPageHandler(w http.ResponseWriter, r *http.Request) {
sess := jeff.ActiveSession(r.Context())
user := loadUser(sess.Key)
fmt.Fprintf(w, internalPage, user)
}
// This page will be reached without an active session if wrapped
// with jeff.Public. ActiveSession will return an empty session if there
// is no session.
func (s *server) publicPageHandler(w http.ResponseWriter, r *http.Request) {
sess := jeff.ActiveSession(r.Context())
user := loadUser(sess.Key)
fmt.Fprintf(w, publicPage, user)
}
Jeff
A tool for managing Sessions
$ redis-cli
127.0.0.1:6379> keys *
1) "c@example.com"
2) "a@example.com"
3) "b@example.com"
127.0.0.1:6379>
Thank you
Alan Braithwiate
Segment
@Caust1c
https://github.com/abraithwaite/jeff
https://blog.abraithwaite.net/2018/08/14/two-sessions/
Tale of Two Sessions
By Alan Braithwaite
Tale of Two Sessions
- 852