Upgrade to Pro — share decks privately, control downloads, hide ads and more …

A Deep Dive into Sessions

A Deep Dive into Sessions

What if your Rails app couldn’t tell who was visiting it? If you had no idea that the same person requested two different pages? If all the data you stored vanished as soon as you returned a response? The session is the perfect place to put this kind of data.

But sessions can be a little magical. What is a session? How does Rails know to show the right data to the right person? And how do you decide where you keep your session data?

Justin Weiss

April 25, 2017
Tweet

More Decks by Justin Weiss

Other Decks in Programming

Transcript

  1. @justinweiss #deep-sessions My Three Phases of Understanding 1. Do more

    of the thing that isn’t working 2. Avoid the thing that isn’t working
  2. @justinweiss #deep-sessions My Three Phases of Understanding 1. Do more

    of the thing that isn’t working 2. Avoid the thing that isn’t working 3. Learn the thing! (And use it correctly)
  3. @justinweiss #deep-sessions HTTP/1.1 200 OK Date: Wed, 08 Mar 2017

    22:57:05 GMT ... X-Params-Breed: Persian ...
  4. @justinweiss #deep-sessions HTTP/1.1 200 OK Date: Wed, 08 Mar 2017

    22:57:05 GMT ... X-Params-Breed: Persian ...
  5. @justinweiss #deep-sessions $ curl -I http: // www.google.com HTTP/1.1 200

    OK Date: Wed, 08 Mar 2017 22:57:05 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=ISO-8859-1 Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Set-Cookie: NID=98=T ... Transfer-Encoding: chunked Accept-Ranges: none
  6. @justinweiss #deep-sessions $ curl -I http: // www.google.com HTTP/1.1 200

    OK Date: Wed, 08 Mar 2017 22:57:05 GMT Expires: -1 Cache-Control: private, max-age=0 Content-Type: text/html; charset=ISO-8859-1 Server: gws X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Set-Cookie: NID=98=T ... Transfer-Encoding: chunked Accept-Ranges: none
  7. @justinweiss #deep-sessions < HTTP/1.1 200 OK < … < Set-Cookie:

    NID=98=T ... > GET / HTTP/1.1 > Host: www.google.com > ... > Cookie: NID=98=T ...
  8. @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  9. @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  10. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=OXJ2SkhNaFZBWDd1eDU3djhSekZRdmN6WjNKUjN4dlBiMWt3 bW9sVjM0OERIZ3lPUmV1UFB2MmlySzI0OXJtbTRDdmI3TGd0S3AvMVNjdTlueEo1 Y05zMnE3NTdsMVVmWWFVSXA5NVFOT0U9LS1tM21SL2tIMGhxYjFEWjZjb2Y3ZWln PT0%3D --533f89e5525959c122e31ff7eae5b886b2ed7fe9; path=/; HttpOnly
  11. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=OXJ2SkhNaFZBWDd1eDU3djhSekZRdmN6WjNKUjN4dlBiMWt3 bW9sVjM0OERIZ3lPUmV1UFB2MmlySzI0OXJtbTRDdmI3TGd0S3AvMVNjdTlueEo1 Y05zMnE3NTdsMVVmWWFVSXA5NVFOT0U9LS1tM21SL2tIMGhxYjFEWjZjb2Y3ZWln PT0%3D --533f89e5525959c122e31ff7eae5b886b2ed7fe9; path=/; HttpOnly
  12. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=OXJ2SkhNaFZBWDd1eDU3djhSekZRdmN6WjNKUjN4dlBiMWt3 bW9sVjM0OERIZ3lPUmV1UFB2MmlySzI0OXJtbTRDdmI3TGd0S3AvMVNjdTlueEo1 Y05zMnE3NTdsMVVmWWFVSXA5NVFOT0U9LS1tM21SL2tIMGhxYjFEWjZjb2Y3ZWln PT0%3D --533f89e5525959c122e31ff7eae5b886b2ed7fe9; path=/; HttpOnly
  13. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=OXJ2SkhNaFZBWDd1eDU3djhSekZRdmN6WjNKUjN4dlBiMWt3 bW9sVjM0OERIZ3lPUmV1UFB2MmlySzI0OXJtbTRDdmI3TGd0S3AvMVNjdTlueEo1 Y05zMnE3NTdsMVVmWWFVSXA5NVFOT0U9LS1tM21SL2tIMGhxYjFEWjZjb2Y3ZWln PT0%3D --533f89e5525959c122e31ff7eae5b886b2ed7fe9; path=/; HttpOnly
  14. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=OXJ2SkhNaFZBWDd1eDU3djhSekZRdmN6WjNKUjN4dlBiMWt3 bW9sVjM0OERIZ3lPUmV1UFB2MmlySzI0OXJtbTRDdmI3TGd0S3AvMVNjdTlueEo1 Y05zMnE3NTdsMVVmWWFVSXA5NVFOT0U9LS1tM21SL2tIMGhxYjFEWjZjb2Y3ZWln PT0%3D --533f89e5525959c122e31ff7eae5b886b2ed7fe9; path=/; HttpOnly
  15. @justinweiss #deep-sessions secret = Rails.application.key_generator .generate_key("encrypted cookie") sign_secret = Rails.application.key_generator

    .generate_key("signed encrypted cookie") encryptor = ActiveSupport ::MessageEncryptor.new( secret, sign_secret, serializer: ActiveSupport ::MessageEncryptor ::NullSerializer)
  16. @justinweiss #deep-sessions • Rails stores session data inside a single

    cookie • By default, the session data is turned into JSON • Rails signs and encrypts the cookie • The session key and serializer can be configured to be something different
  17. @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  18. @justinweiss #deep-sessions curl -H "Cookie: _session_my_app={\"book\": Call me Ishmael. Some

    years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off— then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me. There now is your insular city of the Manhattoes, belted round by wharves as Indian isles by coral reefs— commerce surrounds it with her surf. Right and left, the streets take
  19. @justinweiss #deep-sessions curl -H "Cookie: _session_my_app={\"book\": Call me Ishmael. Some

    years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people’s hats off— then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me. There now is your insular city of the Manhattoes, belted round by wharves as Indian isles by coral reefs— commerce surrounds it with her surf. Right and left, the streets take
  20. @justinweiss #deep-sessions 1. Store the data in the cookie 2.

    Store a reference to the data in the cookie
  21. @justinweiss #deep-sessions Creating an ActiveRecord session 1. Generate a new

    random session id 2. Turn the session hash into a string 3. Save the id and data to a sessions table in your database 4. Return the session id with Set-Cookie:
  22. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=a6c4946995fe1a7fe0e472610c368858; path=/; HttpOnly Hello, Justin!
  23. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=a6c4946995fe1a7fe0e472610c368858; path=/; HttpOnly Hello, Justin!
  24. @justinweiss #deep-sessions $ curl -i "http: //localhost:3000/users/greet?name=Justin" HTTP/1.1 200 OK

    ... Set-Cookie: _session_my_app=a6c4946995fe1a7fe0e472610c368858; path=/; HttpOnly Hello, Justin! session_id | data a6c4946995fe1a7fe0e472610c368858 | BAh7BkkiCW5hbWUGOgZFRkkiC0p1c3RpbgY7AFQ=
  25. @justinweiss #deep-sessions Finding an ActiveRecord session 1. Grab the session

    id out of the cookie 2. Look up the session id in the database 3. Grab the data associated with that id 4. Turn that data back into the sessions hash
  26. @justinweiss #deep-sessions Two kinds of session store • CookieStore: store

    all data in the cookie • Everything Else: store a pointer to data in the cookie
  27. @justinweiss #deep-sessions Two kinds of session store • CookieStore: store

    all data in the cookie • Everything Else: store a pointer to data in the cookie
  28. @justinweiss #deep-sessions Cookie Store • No extra setup • Syncs

    with the browser lifecycle • Can only store 4kb of data • Risk of session replay attacks
  29. @justinweiss #deep-sessions Cookie Store • No extra setup • Syncs

    with the browser lifecycle • Can only store 4kb of data • Risk of session replay attacks
  30. @justinweiss #deep-sessions CacheStore • You probably already have a cache

    • Built into Rails • Fast -- usually kept in memory • Sessions and cache fight for space - sessions can get kicked out • Resetting your cache expires all sessions
  31. @justinweiss #deep-sessions CacheStore • You probably already have a cache

    • Built into Rails • Fast -- usually kept in memory • Sessions and cache fight for space - sessions can get kicked out • Resetting your cache expires all sessions
  32. @justinweiss #deep-sessions CacheStore • You probably already have a cache

    • Built into Rails • Fast -- usually kept in memory • Sessions and cache fight for space - sessions can get kicked out • Resetting your cache expires all sessions
  33. @justinweiss #deep-sessions CacheStore • You probably already have a cache

    • Built into Rails • Fast -- usually kept in memory • Sessions and cache fight for space - sessions can get kicked out • Resetting your cache expires all sessions
  34. @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  35. @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  36. @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  37. @justinweiss #deep-sessions # == Schema Information # # Table name:

    cart_items # # id :integer not null, primary key # title :string # quantity :integer # created_at :datetime # updated_at :datetime # class CartItem < ActiveRecord ::Base ... end
  38. @justinweiss #deep-sessions # == Schema Information # # Table name:

    cart_items # # id :integer not null, primary key # name :string # quantity :integer # created_at :datetime # updated_at :datetime # class CartItem < ActiveRecord ::Base ... end
  39. @justinweiss #deep-sessions • ❌ Reverse the change • ❌ Delete

    all sessions • ✅ Go into the corner and cry
  40. @justinweiss #deep-sessions • ❌ Reverse the change • ❌ Delete

    all sessions • ✅ Go into the corner and cry
  41. @justinweiss #deep-sessions Complexity layered on • More data? Serialize it

    / Store a pointer. • Tampering? Add encryption. • Cross-site Scripting? Add HttpOnly. • Snooping? Add Secure.
  42. @justinweiss #deep-sessions My Three Phases of Understanding 1. Do more

    of the thing that isn’t working 2. Avoid the thing that isn’t working 3. Learn the thing! (And use it correctly)