We're sorry but this page doesn't work properly without JavaScript enabled. Please enable it to continue.
Feedback

A Deep Dive Into Sessions

00:00

Formal Metadata

Title
A Deep Dive Into Sessions
Title of Series
Part Number
77
Number of Parts
86
Author
License
CC Attribution - ShareAlike 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal and non-commercial purpose as long as the work is attributed to the author in the manner specified by the author or licensor and the work or content is shared also in adapted form only under the conditions of this
Identifiers
Publisher
Release Date
Language

Content Metadata

Subject Area
Genre
Abstract
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?
Product (business)CodeOnline helpSoftware developerCodeXMLUML
Software developerElectronic program guideWebsiteRevision controlInformationDifferent (Kate Ryan album)Mobile WebMereologyMultiplication signDistortion (mathematics)WaveletWeb 2.0XMLComputer animation
Computer-assisted translationArithmetic meanProgrammer (hardware)Exception handlingFunctional (mathematics)Software bugHash functionBitDatabaseData storage deviceWebsiteSingle-precision floating-point formatMultiplication signSheaf (mathematics)Real numberHypermediaACIDComputer programmingProcess (computing)Web 2.0Computer animation
Web 2.0Multiplication signPointer (computer programming)Computer programmingDatabase transactionException handlingGroup actionMobile appCodeBitLoginWeb pageWebsiteModel theoryIdeal (ethics)XMLComputer animation
TrailWeb 2.0Software developerInformationWeb browserEmailServer (computing)Computer configurationInternetworkingUniform resource locatorSolid geometryBitInsertion lossComputer animation
EmailWeb browserHTTP cookieState of matterNumbering schemeServer (computing)MereologyMetadataObject (grammar)InformationData conversionMultiplication signWebsiteGoogolWeb pageMobile appMehrplatzsystemConnected spaceSingle-precision floating-point formatSemiconductor memoryClosed setLine (geometry)Computer programmingSlide ruleUniform resource locatorKey (cryptography)Attribute grammarTrailRevision controlReflektor <Informatik>MathematicsProcess (computing)CodeData storage deviceSampling (statistics)Game controllerGroup actionHazard (2005 film)BitComa BerenicesValidity (statistics)Hash functionDistortion (mathematics)Level (video gaming)CASE <Informatik>Sign (mathematics)Computer configurationVirtual machineDomain nameRule of inferenceWikiOrder (biology)CuboidSet (mathematics)Sheaf (mathematics)Cycle (graph theory)Directory servicePattern languageMobile WebComputer animation
HTTP cookieWeb browserSign (mathematics)Multiplication signData storage devicePoint (geometry)Hash functionEmailWeb pageMobile appFormal languageShared memoryGenetic programmingDefault (computer science)QuicksortParameter (computer programming)Greatest elementServer (computing)Key (cryptography)String (computer science)EncryptionMultiplicationObject (grammar)CASE <Informatik>Link (knot theory)Product (business)CodeElectric generator1 (number)Software testingSerial portDifferent (Kate Ryan album)Buffer overflowException handlingGroup actionSingle-precision floating-point formatProbability density functionMetropolitan area networkRight angleFigurate numberDigital electronicsReal numberFood energySheaf (mathematics)Overhead (computing)Cartesian coordinate systemRadiusBootingInteractive televisionComputer animation
Exception handlingSubsetStrategy gameHTTP cookieData storage deviceAxiom of choiceServer (computing)Web browserWritingInformationLink (knot theory)Shared memoryCache (computing)QuicksortMathematicsCyberspaceDependent and independent variablesGame controllerPartial derivativeMobile appWebsiteCuboidDatabase2 (number)Computer configurationRight angleRandomizationHash functionRow (database)Order (biology)Software maintenanceBookmark (World Wide Web)Multiplication signSemiconductor memoryString (computer science)Sign (mathematics)Process (computing)Decision tree learningEmailElectronic mailing listMatching (graph theory)Limit (category theory)Flow separationGroup actionFilm editingCASE <Informatik>Sweep line algorithmEncryptionReference dataParameter (computer programming)Different (Kate Ryan album)Atomic numberAreaSheaf (mathematics)Cycle (graph theory)Regular graphHD DVDPower (physics)System callCharge carrierSound effectPatch (Unix)Panel paintingXML
Hacker (term)Key (cryptography)HTTP cookieInheritance (object-oriented programming)Computer networkServer (computing)Order (biology)Real number
HTTP cookieOpen setProof theoryComputer networkFacebookRight angleDoubling the cubeConfiguration spaceDomain nameProduct (business)WebsiteCommunications protocolAttribute grammarGoogolReading (process)Public key certificateEncryptionConnected spaceFreewareAngleDifferent (Kate Ryan album)Forcing (mathematics)Process (computing)Direction (geometry)Cellular automatonInternetworkingParticle systemProgram flowchart
MereologyInformationProfil (magazine)Vulnerability (computing)WebsiteBookmark (World Wide Web)MyspaceCyberspaceFigurate numberWeb pageMultiplication signData storage deviceTablet computerHTTP cookieControl flowServer (computing)Game controllerSoftware bugCodeNeuroinformatikObject (grammar)Exception handlingService (economics)InfinityThumbnailAtomic numberRandomizationCache (computing)Rule of inferenceSheaf (mathematics)PlastikkarteNumberoutputData conversionGoodness of fitBit rateFunction (mathematics)Drop (liquid)MathematicsReal numberComputer configurationCASE <Informatik>Functional (mathematics)Information securityProduct (business)Web browserMusical ensembleMoment (mathematics)Sign (mathematics)Software testingCellular automatonState of matterCommutatorComputer programmingDatabaseHand fanAreaDefault (computer science)Different (Kate Ryan album)Tableau2 (number)Electronic program guideLink (knot theory)Mobile appVirtual machineBoss CorporationDecision tree learningCausalityAttribute grammarAsynchronous Transfer ModeComputer animation
Web 2.0Server (computing)Dependent and independent variablesAreaWeb-DesignerWeb browserComputer networkProxy serverInternetworkingHand fanCondition numberElectronic mailing listMobile appConnected spaceHTTP cookieEmailSource code
Domain nameWeb browserWebsiteHTTP cookieData structureCore dumpProduct (business)Entire functionKey (cryptography)Direct numerical simulationLink (knot theory)Software testingAsynchronous Transfer ModeString (computer science)Set (mathematics)Video game consoleInstance (computer science)XMLUML
BitPrimitive (album)Key (cryptography)Core dumpMereologyHTTP cookieDigital electronicsComputer animation
Core dumpStress (mechanics)Bookmark (World Wide Web)Web 2.0Forcing (mathematics)FrustrationMultiplication signSoftware developerExpected valueElement (mathematics)Solvable groupDemosceneTerm (mathematics)Source codeComputer animation
Link (knot theory)Computer programmingEmailSoftware bugMultiplication signTheory of relativitySlide ruleComputer animation
XML
Transcript: English(auto-generated)
Let's get started. Hey everybody, hope your day has been going well so far. Thank you for coming by. I'm Justin Weiss, and I work at Avvo,
where we help people find the legal help that they need. At Avvo, I help our software developers get better code into production more quickly. I also write articles and guides to becoming a better Rails developer on my site, justinweiss.com. And I wrote a book, Practicing Rails, which can help you learn Rails without getting overwhelmed. Hey everyone, hope your day has been going well so far. Thank you for coming by.
I'm Justin Weiss, and wave at me if you've seen this part already. What if that was your experience on the web? Like imagine if your site couldn't know that the same person visited two different times, or if everything you knew about a user just disappeared as soon as you returned that first piece of HTML. Now this might be fine for a site that doesn't do much,
you know, a site that only cares about handing out the most generic information. But most of us, we don't really live in that world, do we? We need to know about our users, and we need to store some data about them, whether that's like a user ID, a preferred language, whether they like the mobile or the desktop version of your site better, or what their favorite breed of cat to make memes with is.
Now you could solve this the functional programmer way. You know, if you can't store a state, then pass all the data you have along with every single request. Whoops. But since we're all using Rails, this problem's pretty easy to solve. We put data in the session hash, and it magically comes back to us in the next request. And with that, we never have to worry about which user is accessing our site ever again.
So this has been my talk and intro into sessions. Thank you all for coming. But wait a second, how does this even work? How does it stick around? I mean, I thought HTTP was stateless. Now Rails makes using sessions really easy, and that's great, but it's also a little bit dangerous. For a long time, I treated sessions like a database that I didn't have to set up.
I didn't understand sessions, but I didn't really need to, because they were a magic hash that I could always depend on, sometimes depend on. And that sometimes, that flakiness, it meant that I hated using sessions. I mean, how many of you get unreasonably frustrated when session stuff doesn't work? When you see session exceptions or session bugs or missing data for a user?
Now why is that? In my first web programming job, a lot of people didn't have accounts, and so we had to use sessions a lot. And I caused so many problems with them. No pointer and couldn't find data for this user exceptions. Those showed up like 10 times as often as any other. And I actually got reprimanded more than once for the problems that I caused around sessions.
So this is what I did. I didn't understand it, so I just did what I was already doing harder. You know, the code I wrote wasn't working. The code I wrote wasn't working, but that's okay, because I'll just write more code. You know, null checks everywhere. Now when that didn't work, I tried to avoid them. You know, sessions are terrible, let's not use them.
I have an idea, let's just make the user log in on every page, it'll be great. But then I got a little bit more mature, and I realized that this problem wasn't gonna go away. So instead, I spent the time to really understand sessions at a deep level and construct a mental model out of them that worked. And after a while, I started to be able to write code that avoided a lot of these problems in the first place. The nice thing is, to understand sessions,
we really don't need to get too complicated. Really, this is what we want. We want to know about a user, we want to know about them securely so that nothing can mess with that data, and we want to know about it until they leave. Once they stop using the site, we don't really need to keep that data around anymore. All we need is some way for your user's browser to coordinate with your Rails app and make everything connect up.
Now this problem, the problem of not being able to keep track of information about users, like people realized this was a thing pretty early on. And as more people wanted to use the web, and especially to buy things on the web because money, people, developers needed a way to keep track of things like shopping carts and user preferences. Now I was joking a little bit earlier about passing in all the data you needed
along with every request through the URL. But that's actually not too far off from what we ended up doing. If those params are in the URL though, it's easy to see them, it's easy to lose them, and it's easy to fake them. You have another option though. When a browser makes a request to a server, it sends some headers along with that request, some information about that request. So what if browsers send some user data
along with the rest of the headers so the server could see it? Once the server saw that data in the headers, it could use it, it could change it, and it could send new data back to the browser. Then the browser could send that modified copy back to the server, and they could just kind of ping pong it back and forth, changing it as they needed to. Now this idea, the idea to use a header that would be automatically sent from the browser,
this came out of Netscape in the early 90s. And Netscape called these special headers cookies. And about a year later, it was supported in IE. So yeah, even 20 years ago, we had to deal with that. But the neat thing about this is that the server really doesn't have to manage any of this data at all. This data is stored with the browser and managed by the browser. So how do these cookies work?
Like what do they look like? Well let's say you make a request to Google through your browser, or because these are slides through a program like cURL. When Google sends you the page, it also sends you some HTTP headers, like that metadata about a request. And there's one line I want you to focus on right now. This one. When Google returns a page, it also sends your browser that set cookie header.
When your browser sees that, it stores it along with information about which server it came from. In this case, Google.com. That's so that the next time you request a webpage from Google, your browser will send headers like this. And if we put these side by side, you can see it sends that exact same cookie the next time your browser hits Google. That way, your browser and your server have some shared piece of information.
They have that connection. They can keep their conversation going and not have to reintroduce themselves to each other every single time. Now all cookies have a few different parts. They have data, which is the information that your server wants to remember. And they have metadata, which the browser cares about, which determines how and when that cookie should be sent to a server.
This first part, the part before the semicolon, is the data. When you use the session object in Rails, it's storing data in that part and it's reading data out of that part. The rest of it is all metadata. For example, you can give cookies an expiration date. In this case, after September 7th, the browser won't send this cookie anymore,
which means that the server won't have access to the data inside that cookie anymore. If you don't set an expires date, the cookie will usually disappear as soon as the browser closes. These are called session cookies because they last for one browser session and then get deleted when you close the browser. They're sometimes even just stored in the browser memory and not actually persisted anywhere because you don't really need to.
Cookies that have an expires date are sometimes called permanent cookies because they last until the date you set. They're not automatically cleared when you close the browser. Now sites can't read each other's cookie data, so if I was visiting goggles.com instead of google.com, this cookie wouldn't be sent. With path and subdomain, you can go a little bit further. You can go down to subdirectories and subdomains,
which is especially helpful if you end up running multiple apps run by multiple users on a single domain, like think GitHub pages or WordPress. There's a leading dot there, but .google.com is the same thing as google.com. It's safer to have it because older browsers still care about it. Now when you set a domain, it also includes all subdomains. So here the cookie is valid for google.com
and all the subdomains, drive.google, docs.google, all that stuff. And there's also some extra attributes you can give cookies like HTTP only that I'll go over a little bit later on. But you didn't just come here to learn about cookies and get hungry before lunch. What does all of this have to do with sessions? Well sessions are built using cookies because cookies are a pretty reliable way
to keep track of users without having to keep track of params. So the better you understand cookies, if you can understand cookies at a deep level, it's going to be much, much, much easier to understand sessions and how they work and how they don't. We'll see in a minute that just like a hash, you can build way more complicated things on top of cookies. But out of the box, they're pretty limited.
A single cookie can hold only a single value, so you need a separate cookie for every key value pair. And because all of the information is on the user's side, on the browser's side, you can't necessarily trust what they give you. Like what if you stored username inside a cookie? The server would send this to your browser, then you go into your browser's cookie database and change it to this.
Your browser sends this cookie to the server and all of a sudden you can mess with everybody's data because the server doesn't know any better. Like it can't know any better, it has to trust this. Now this isn't any safer than having it in the URL like we saw before. So how does Rails get around these problems? Well let's dig into an example. Here we have a pretty simple controller action. It takes whatever's in param's name, like whatever's passed as a name param,
and puts it into the session under session name. This way we should be able to see some session data get created. So let's hit that and see what we get back. You can see that we're sending the name just into the controller. Again, our controller's going to take that name param, put it into the session, and then hopefully return that session back. And that's maybe what we see over there. You can see that Rails stores the session
under a single key, like a single cookie, this session myapp cookie. And if you search the sample code base for session myapp, you'll find it in an initializer, session store.rb. If you change that option, it'll change which key your session data is stored under and also break all your old sessions in the process. So not necessarily a great idea, but you can certainly do it.
Now what about the rest of the cookie? Like I said, we maybe have the data in there, and that's because we can't really tell. It's totally unreadable. I mean, somebody can try if they want, but it's probably not gonna be too successful. So how does it do that? Why does Rails do that? Well, the why is pretty easy to answer. The value looks like that so that users or anybody else can't mess with their own cookies.
In modern versions of Rails, session cookies are signed, and that means that if anybody tampers with them, they become invalid. And they're also encrypted so that nobody can even see the data stored inside them. But that's not gonna stop us. Let's try to get into this cookie and see what's inside. Now all Rails apps have a secret key. These are these things that get constantly
leaked onto GitHub. And the key is also used for all the encryption that Rails does, which is why it's such a disaster when it gets leaked onto GitHub. But that includes encrypting and signing cookies. And Rails gives you some default, or generates some default secret keys for dev and test. You can generate new ones for production with rake secret. When you're at boots, Rails puts the secret key
into the key generator here at rails.application.keygenerator. Don't bother writing any of this down. I have code later on, or I have links later on. But you can see we use that key generator, that rails.application.keygenerator at the top to create some secrets. And then we use that secrets to create an encryptor object.
And this encryptor object is the same kind of thing that we use to encrypt and decrypt cookies. So with this encryptor object we have at the bottom here, we should be able to decrypt our cookie. So we can paste that big giant string of encrypted text into this encryptor object, and this is what we get. Looks like JSON, right? So why is that JSON? Well it turns out you can also configure that.
It's set inside another initializer, this cookies serializer. You can also use the symbol Marshall, if you'd rather use Ruby Marshall. But most people just use JSON, it's the default. It's also even useful if you're trying to share cookies between apps written in other languages. Because every language understands JSON at this point. So what do we know now?
We know that Rails stores all the session data inside a single cookie. We know that it does this by turning it into JSON, which gives us the opportunity to put multiple keys and values inside a single cookie. We know that Rails signs and encrypts the cookie, so you can't tamper with it or look at it. And we know that the session key and the serializer can both be configured to be something different
if you want to. So now we can see that the cookie actually does contain that name param that we passed way back then. And what we should be able to see now is if we stop passing that param and we pass this cookie instead, we should be able to see this data come from the session data and not from the param. And so let's check that out.
Here we pass that big blob of encrypted cookie back to Rails, and it should remember who we are without a parameter. You see we dropped the parameter off of the URL at the bottom there. And that's what we see. And we didn't have to pass any params. This is exactly the sort of thing that a browser would be doing in this case to store that data across multiple requests. So step by step to kind of tie everything together,
your browser hits a server and the server stores data into the session. Rails turns that session data into JSON. It encrypts the JSON and it signs it. And then it sends the encrypted cookie back to the browser in that set cookie header. The browser then stores it along with the fact that it came from your Rails app. That's so that the next time you hit your Rails app,
the browser will send that cookie back. Rails will verify and decrypt it and then turn it into that session hash that everybody can use. It's like params that are passed on every page but managed automatically so that you don't have to think about it. Finally, Rails can change the data and send it back to the browser which will overwrite the previous cookie and they can just keep passing that data back and forth.
But if passing cookies back and forth was all there was to sessions, there'd be no reason to call them sessions. I mean, you'd just say, hey, this cookie, this one is your session cookie. Like, it's just the same thing. But cookies aren't always right. Remember how your browser sent that cookie along with every single request? Well, what happens if you start storing a ton of data inside of that cookie?
Like, what happens if you store a four-meg PDF inside the cookie? Or like, the full text of Moby Dick for some reason? Every request to your server would include that four megs of data even if your server didn't care about it right now. Even if your server didn't read it during that request. So cookies are limited. You can only put four kilobytes of data in there. If you store more than that, you're gonna get an exception. This action dispatch cookies cookie overflow exception
which also happens to be the most delicious of all of the Rails exceptions. But even 4K is a whole lot bigger than most HP requests. I mean, most requests are only a couple hundred bytes. This is like 10 times that size. So if you care about performance, you probably don't wanna get even close to that 4K limit. But what if you needed to store more data than that inside your session?
How can you keep your cookies small but make your sessions big? Well, let's think about how you're already dealing with users. If your cookie stored a user, you're probably storing a user ID in there. You're not storing their email address in there, you're not storing their full name in there, you're not storing their list of cart items in there. You're just storing their user ID and then you'll use that ID to look up other information inside their database later on.
But what about people who don't have an account? They don't have a user ID, so you can't do this. But you could generate a random ID and you could store that in the cookie like this. Then you could use that ID in the exact same way that you're using user ID to look up information from the database later on. It's not really a user ID though, so you should probably call it something different,
in this case, a session ID. So now we have two different options for storing data persistently across multiple requests. You can store the data right inside the cookie, or you can store a reference to that data inside the cookie and store the actual data someplace else, like inside a database. Now what would that second option look like? Well let's say that, just like the rest of our data,
we wanna use active record to store our session data. And let's say we called session name equals Justin to create a brand new session. What would Rails have to do in order to store this in active record? Well, Rails could generate a new random session ID, so it has something to look it up with. It could turn the session hash into a string so that you don't have to have like separate columns for everything you could possibly store in the session.
You just stuff it all in one string and then put it in a single column. It would save the ID and that data to a row in your database, so you could look it up later and then it would return the ID with set cookie so that the next time your browser hit your site, you could use that ID, look up your session data and get your session hash back. So let's take a look at that in action.
First we'll change our session store to the active record store, which is a gem, like that. Then we'll add some data to the session using curl again. Remember when we passed that name param, it just takes it out of the name, puts it into the session. And this time we're just getting a short string returned instead of that big mess of encrypted sign data, that A6C49 string instead of that big mess.
And that comes from, if you look inside your database, you'll see a session ID alongside some encoded data. And you'll notice that the session ID and the string returned to the browser match. And that's how we'll use that ID to look up the session data later on. When your browser sends that cookie data back to the site
it remembers who we are, again without passing in a param. And this is how that works. It grabs the session ID out of the cookie, it looks up the session ID in your database, it pulls the data that's associated with that ID, and then it transforms that data back into your session hash.
You can even store sessions in memcache and Redis and MongoDB or pretty much anywhere else. And they all pretty much follow the same process. Your cookie is now just a session ID, and then your app uses that ID to look up the rest of the information. And you can even write your own session store. You just need to tell Rack how to find sessions, how to create new sessions, write session data, and delete sessions by implementing a couple methods.
And Rails even includes a simple cache store that uses your Rails cache to store sessions. And it's a really, really simple and good example to follow if this is something that you're interested in. I'll have a link to that in the notes that I'll share at the end. And that's really the gist of how Rails stores sessions. There are two different strategies it uses. There's the cookie store strategy and there's the everything else strategy.
No matter what, you're storing some data inside the cookie because you have to. It's the way that the browser keeps that relationship with the server. But while cookie storage stores all the data inside the cookie, the other methods just store reference to data inside that cookie, and then they can store the data however they want, like in database, on disk, in memory, wherever. But now we have a choice to make
because we have a few different ways to store sessions. And this is an important choice to make because changing session stores is not an easy thing to do. So which one should you choose? Should you choose the cookie store, the cache store built into Rails, or the database store? Well, storing your session data in cookies is by far the easiest way to go.
You don't need to do any extra infrastructure setup. It just kind of works out of the box. It's also nice because it syncs with your user lifecycle. And by that I mean, while your user's visiting your site, it's active. When your user stops visiting your site, if they never visit your site again, you have no cleanup to do because the cookie's on the browser side, not the server side. No other methods can guarantee that, and it saves you some cleanup.
But it's also limited. You can only store 4K of data, and you probably don't want to go anywhere near that. And it's also more vulnerable to certain kinds of attacks, which I'll go into a little bit later on. But if the cookie store won't work for you, you have two options. You can store sessions in a database, or you can store them in your Rails cache. Now you might already be using something like memcache to cache your partials or some API response data
or that kind of thing. And if you are already using a Rails cache, then this is pretty easy too. It's already set up for you. You don't have to do any more extra infrastructure work or any of that kind of stuff. You also don't have to worry about your session growing out of control because most good caches are going to evict older stuff when new stuff comes in. And it's fast because if your cache is slow,
you probably have bigger problems to solve. But it's also not perfect. Your sessions and your cache data are going to be fighting for space. And if you don't have enough memory, you could be facing a ton of early cache misses and early expired sessions. And if you ever need to reset your cache, like let's say you upgraded Rails or you made a big sweeping change around your site,
and you just wanted to wipe everything and start over, you can't do that without also wiping your sessions. Still, this is how we tend to store data on our main novel.com site, and it's worked pretty well for us so far with those caveats. Now if you want to keep your data around until it legitimately expires, you probably want to keep it in some sort of database, whether that's like Redis or whatever you're using for active record or something else.
But storing sessions inside your database has some other problems. Sessions won't get cleaned up automatically, so you'll have to go through and delete old sessions on your own. You also have to know how your database is going to work when it's full of session data. Like are you using Redis as your session store? Is it going to try to keep all of your session data in memory? Does your server have enough memory for that,
or is it going to start swapping so hard that you can't SSH in to fix it? It's happened to me. You also have to be more careful about when you create session data, or you'll fill your database with useless sessions. Like for example, if you accidentally touch the session on every single request, when Googlebot crawls your site, they could be creating hundreds of thousands of useless sessions that are never going to be hit again. And that would be a bad time.
Now most of these problems don't happen super frequently, but they're all things that you need to think about if you're storing the session data semi-permanently. Now if you're pretty sure you won't run into any of the cookie store's limits, cookie store is my favorite. You don't need to set it up, and it's not a headache to maintain. Cache versus database I see as more of a choice of how much you want to do maintenance versus how much you worry about
accidentally expiring sessions early. I tend to treat session data as pretty temporary. I tend to program pretty defensively around sessions. So the cache store works well for me. So my personal preference is cookie store first, then cache store, then database store. Now in a lot of these examples, we've used sessions to identify users. And that's actually one of the more common things
you're going to use sessions for. And that also makes it a super juicy target for hacking. That means on top of the pretty simple key value pair that make up cookies and sessions, there's a lot of extra stuff that somebody needs to worry about in order to keep your cookies secure. Now I talked about how the Rails server trusts your cookie. It kind of has to, because it's the only thing it has to go on. And that means that if somebody else
can get your session cookie, the Rails server has no way to tell that they're not actually you. Now in lots of public Wi-Fi networks, you could pretty easily snoop on other people's network traffic. And so if you're sending cookies to an insecure, over an insecure network to insecure servers, somebody might be able to grab your cookies and pretend that they're their cookies. This became a pretty big deal a couple years ago
when a guy, Eric Butler, released a proof of concept called FireSheep that would grab cookies over open Wi-Fi networks. And check this out. Double click on somebody and you're instantly logged in as them. That's scary, right? I mean, Facebook used to have this happen to them. Now the only way to really prevent this is to run your site over HTTPS. And that way, all of your cookie data
and all your session data is secured along with the rest of your internet traffic. On the Rails side, you can turn this on pretty easily. There's some extra infrastructure setup you'll have to do, though. But you flip this config for SSL equals true in your production.rb. And with free SSL certificates from Let's Encrypt and this whole ecosystem building up around them, and I think Heroku now supporting SSL on all paid dynos,
there's really not a great excuse to run a site without SSL anymore. After you force SSL on, Rails will automatically add this attribute to your cookies, this secure attribute. What this means is that your cookies will no longer be sent to HTTP protocol sites. It's only going to be sent to HTTPS. It works just the same way
as if you were trying to send a cookie to a different domain. But snooping a Wi-Fi connection isn't the only way to steal somebody's cookies. Because JavaScript can also read cookies. That is, if you're Google, you can use document.cookie to read google.com cookies. And anybody else on Google that can run JavaScript on Google can also read Google cookies and send them to whatever server they want.
Now MySpace is probably my favorite example of this. MySpace is my favorite example of a lot of things, but this was particularly fantastic. So that site used to have scripting vulnerabilities all over the place. It was really easy to get JavaScript or Flash embedded on your profile. And once you did that, you could grab information about any of the people that hit your page, like name, profile URL, account ID, all that kind of stuff.
I'm guessing that you probably could've even logged in as them, but nobody that I knew figured that part out. Rails protects you from a lot of these attacks automatically by escaping your HTML. And Rails also marks session cookies as HTTP only by default. What that means is that when a cookie is marked as HTTP only, it's only going to be accessible to your server. The browser's not gonna make it accessible to JavaScript anymore.
So that helps with a lot of these things. But if that's not enough, you can't even trust your own users. Like say you run a music store and your customers can earn credit to buy songs. Your boss read an article saying that forcing signups drops conversion rate, so no more signups. We're just gonna put everything in this session instead. Seems great until one of your users gets this brilliant idea. But Justin, you probably say,
we've already went over this. Rails encrypts and signs the cookie so you can't tamper with it. But in this case, you don't actually have to tamper with it. Imagine the cookie was encrypted. You couldn't actually get into this. Somebody buys a song sending this cookie with 400 credits into it. You respond with a new cookie that has 300 credits into it. Your user ignores that cookie and sends the old one again for 400 credits.
Now they have infinite credits because it's never gonna go down. Now this one doesn't really have an easy fix. You can store a unique number in the session and then check to make sure that you never use a number more than once, which is not really my favorite thing to do. Or you can switch to a database store or cache store, which doesn't really have this problem because all of that data is stored on the server side.
But a better idea is just not to put this data in the cookie to begin with. That's what we have databases for, for storing this kind of data. Now these are some of the more interesting attacks, but there's a whole lot more. And I'm a big, big fan of the Rails security guide for learning more about this kind of thing. I'll have a link to that in the notes also. Now it might seem like there's a lot to think about around cookies and sessions.
But there are a few good rules of thumb that I've picked up over the years that tend to keep problems to a minimum. The first is prepare for the session to disappear at any time. And this happens because sessions are on the user's computer. And that's a problem because that means you have absolutely no control over when they clear their cookies or when they switch devices or any of that stuff.
So keep in mind, every time you use a session, that session might not be there anymore. Program defensively because it's going to happen and if you're not prepared for it, it can cause big problems later on when you're not seeing the data that you expect. The second is don't store actual objects in your session.
So why would this be a bad idea? Well let's say you store a cart item in the session that has a title and a quantity. And later on you rename title to name because frankly title is a terrible name for a cart item and I have no idea who came up with that. This is probably going to work great for you in dev. You probably don't even have a cart item in your session in dev mode. But then when you shift to production, cart items in old sessions
are going to try to turn into new cart items. They'll try to put data into a title attribute that no longer exists and everything will explode. Now I've personally taken down large chunks of site because of this problem and I know I'm not the only one. I've seen this happen many times. And when it happens you really only have two options and they're both terrible. One is you could reverse the change
which probably isn't going to work because now you have people with old cart items and new cart items in their sessions and so you can try to come up with some grand unified cart item that deals with both. The whole time the site's falling down and everybody's hair's on fire and it's just a bad time. Or you could just say we're gonna start from a clean slate, we're going to wipe all the session data and we're going to log everybody out and lose all their data.
So I usually go for this third one over here at least for a little while. Now the bigger your objects are the more likely this is to happen. This never shows up in dev and test because you probably aren't using sessions in the same way in dev and test as you are in production. It's the ultimate works on my machine and it will wreck everything when it shifts. So just don't do it. Prefer storing references to objects,
object IDs in the session and not the objects themselves. And finally be deliberate about what you use the session for. Only use the session when it makes a lot of sense. Because sessions are so easy it's really easy for them to become a dumping ground of random data. And that's when things start to go really wrong. One of the worst bugs I ever investigated started like this. We shipped something and we all of a sudden
started to see exceptions coming from what seemed like a completely random part of the app. Like a lot of session bugs we couldn't repro it locally and we couldn't debug it remotely. And after adding a bunch of logging we ended up discovering that some code that we deleted a long time ago used the same name for something in the session as something that we just recently shipped. We had two completely different pieces of data that were stomping on each other and causing problems.
And it turned out that neither of those things needed to be in the session. We just put it in the session because it was convenient and we didn't want to roll a whole new database table for them. And that convenience ended up costing us way more dev time and the experience of some of our users than it would have been just to do it right in the first place. Just like code, if you don't use sessions for something it can't cause a problem.
So use it with intent. Even when you follow these best practices though things are gonna go wrong, they just will. So how do you start debugging when you're not seeing what you expect? Well the best trick I've ever learned to help me debug any kind of problem is to isolate the problem area as quickly as possible. And by that I mean is a function getting the right input?
You probably don't need to go any higher than that. Is a function sending the right output you'd expect given its input? You probably don't need to go any lower than that. And you just keep cutting those closer and closer until you really narrow in on the place that's causing the problem. So the best tools I've found for debugging session issues are all about showing me what my server's sending and what my server's receiving. So how many of you are using something like cURL
or Postman or PAW in your web development? Yeah, that's just many, many people. So these are great tools for debugging session issues. You can see the session data your server's sending and you can send arbitrary sessions back to the server and see how it responds. If cURL, Postman or PAW are telling you that your server is working okay you can usually assume that it's a problem with the browser not sending something you expect
or something else that's weird going on. And for debugging weird internet problems MITM proxy is my favorite tool. So with MITM proxy it's a little server that sits between your browser and your app and it shows you all of the network connections that go on between the two of them. So you can see like a list of network requests. You can dive into each of these things and start to see the request headers,
the response headers, all that kind of thing. You can see all of the real stuff that's going back and forth which is really great at helping to plug these kinds of problems. And even just last week I was debugging a session race condition where we had Ajax requests that were stomping on session data. And with MITM proxy I was able to within about a half hour construct an actual timeline
of how these requests were going through when they were coming back and how they were conflicting with one another. So I'm a big big fan of this tool. If your browser isn't sending cookies correctly nine times out of 10 the domain settings on your cookie are wrong. This is really easy to mess up. It's also really hard to test in dev because in dev mode you're probably not running the entire DNS structure of your production website.
And it's also going to be hard to debug your cookie data if you can't see what it is. So if you're using cookie sessions I have the beginnings of a gem that you can install in dev mode. If you include the gem and run rails console inside your rails app you can paste in your cookie strings and decrypt it using the rails key generator. And I'll have links to all this stuff in the talk notes too.
Now from all this we can kind of see that sessions are core to the modern web. And by modern I mean since like 1995. So modern-ish. And when you run into problems with session data it might seem like they're big, they're complicated, they're flaky, they're frustrating. But like we saw, session data isn't that big of a thing. Sessions are based on a pretty simple primitive.
You know, a single key, a single value, and some metadata. And on that foundation you build new features bit by bit and piece by piece. First you serialize the data so that you can store, the value so you can store more data into a single cookie. Then you encrypt it to avoid tampering with it. Or you use the cookie value as a reference to data somewhere else. Sessions get big and complicated
but really at their core they're built out of a few simple parts that all can be combined together. And this is one of my favorite things about software development is that it's all just code. And things that seem super complicated like Git or sessions or how the web even works, they were all built by somebody. They were built for a reason. They were built to solve a problem. And when you understand what that problem is and how they were built
they're all at their core understandable. We usually only dig into these things when we have problems with them. And that stress and that confusion it makes these seem completely insurmountable. But in the end they almost always turn out to be way more simple than you'd expect. So the next time you get unreasonably frustrated when something doesn't work don't be like me. Skip straight to that last phase.
Spend some time and force yourself to learn it. Turn that frustration into a mystery to solve and dig into the pieces until they're small enough to understand. That's how you'll transform bugs that seem confusing, random and unfair into some new and exciting piece of knowledge you can use for the rest of your programming career. And if you've recently learned something new and exciting or wanna talk programming or really anything else
I would love to talk with you. My email address is up here. Use it, I love getting email. I read and respond to everything. Let me know if you're ever in Seattle I'd love to grab a coffee with you. And that last link up there if you're gonna write one thing down from this write that down. It's a link to resources for the talk with the slides, gem to decode, encrypted sessions and some other notes that didn't quite fit in and some useful session related links.
So it looks like I have about eight minutes or so for some questions if anybody has them. I'm happy to answer them or deflect them in some creative way. All right, well thank you so much again for the time.