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?

Ac54c2b179cd4c54305846de2cb22ba1?s=128

Justin Weiss

April 25, 2017
Tweet

Transcript

  1. 5.
  2. 7.
  3. 14.
  4. 16.
  5. 17.

    @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
  6. 18.

    @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)
  7. 20.
  8. 22.

    @justinweiss #deep-sessions HTTP/1.1 200 OK Date: Wed, 08 Mar 2017

    22:57:05 GMT ... X-Params-Breed: Persian ...
  9. 23.

    @justinweiss #deep-sessions HTTP/1.1 200 OK Date: Wed, 08 Mar 2017

    22:57:05 GMT ... X-Params-Breed: Persian ...
  10. 24.
  11. 27.

    @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
  12. 28.

    @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
  13. 30.

    @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 ...
  14. 38.
  15. 48.

    @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  16. 49.

    @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  17. 50.

    @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
  18. 51.

    @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
  19. 52.

    @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
  20. 54.

    @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
  21. 55.

    @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
  22. 56.
  23. 59.

    @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)
  24. 63.

    @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
  25. 65.

    @justinweiss #deep-sessions class UsersController < ApplicationController def greet session[:name] =

    params[:name] if params[:name] render plain: "Hello, #{session[:name]}!" end end
  26. 74.

    @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
  27. 75.

    @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
  28. 83.

    @justinweiss #deep-sessions 1. Store the data in the cookie 2.

    Store a reference to the data in the cookie
  29. 85.

    @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:
  30. 88.

    @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!
  31. 89.

    @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!
  32. 90.

    @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=
  33. 92.

    @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
  34. 95.

    @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
  35. 96.

    @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
  36. 98.

    @justinweiss #deep-sessions Cookie Store • No extra setup • Syncs

    with the browser lifecycle • Can only store 4kb of data • Risk of session replay attacks
  37. 99.

    @justinweiss #deep-sessions Cookie Store • No extra setup • Syncs

    with the browser lifecycle • Can only store 4kb of data • Risk of session replay attacks
  38. 100.

    @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
  39. 101.

    @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
  40. 102.

    @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
  41. 103.

    @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
  42. 104.

    @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  43. 105.

    @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  44. 106.

    @justinweiss #deep-sessions DatabaseStore • Keeps session data around until expiration

    • Need to clean up expired sessions • Might have capacity issues as session data grows
  45. 108.
  46. 110.
  47. 111.
  48. 117.
  49. 130.

    @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
  50. 131.

    @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
  51. 132.
  52. 133.

    @justinweiss #deep-sessions • ❌ Reverse the change • ❌ Delete

    all sessions • ✅ Go into the corner and cry
  53. 134.

    @justinweiss #deep-sessions • ❌ Reverse the change • ❌ Delete

    all sessions • ✅ Go into the corner and cry
  54. 142.
  55. 145.
  56. 149.
  57. 150.

    @justinweiss #deep-sessions Complexity layered on • More data? Serialize it

    / Store a pointer. • Tampering? Add encryption. • Cross-site Scripting? Add HttpOnly. • Snooping? Add Secure.
  58. 153.

    @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)
  59. 154.