Browser Cookies - A Closer Look
Not long ago, a colleague of mine got stuck on an interesting bug that he really struggled with. A big part of solving it would have been a lot quicker and easier with a deeper understanding of cookies and just how they work. Because he’s a smart guy, I realized it wasn’t a problem of understanding, but rather because the subject of cookies became ‘low level.” So let’s really go over it thoroughly. But if you really understand just how cookies work, feel free to skip ahead a bit.
So what are cookies? A cookie is essentially textual information saved on the client side. We can make cookies a few different ways, with the most well known being via JavaScript. The easy way is a straightforward manipulation of document.cookie.
document.cookie = 'hello'
If you go into the developer tool, you can see that ‘hello’ made it in, but without a name.
Really, that could be enough, but in general it’s good practice to give a name and expiration date to the cookie. This let’s the browser know when to delete the cookie, and makes it easier for developers to read the data. For example:
let a = new Date();
a = new Date(a.getTime() +1000*60*60*24*365);
document.cookie = `mycookie=somevalue; expires=${a.toGMTString()};`;
Creating a cookie with an expiration date
What’s important to note here is that all the data is essentially in strings, and this makes the whole thing a bit more complicated and not so intuitive. But just for this, we have sessionStorage and localStorage—so we can easily store data on the client side without cookies. So why then do we need cookies? Ah ha ha! Cookies have a few important properties that are very important to know about.
The first is that the cookie is sent with every HTTP request to the server. It’s easy to check this. Run the code above in your console—it will create the cookie mycookie=somevalue. Refresh the page and have a look at your request. You can see the request was sent with the cookie.
The cookie appears in the request ‘headers’
This is really useful for dealing with authentication. The cookie is not sent by some magical voodoo spell—it’s sent as a legitimate part of the headers of the request along with the GET parameters, the user agent, and the rest of the gang. Totally legit!
The second property is the possibility to make the cookie ‘secure.’ This means it won’t be sent if the domain is HTTPS. This doesn’t really give that much security, but the real party begins with httpOnly. Setting a cookies as httpOnly makes it so that it’s only sent with the request and prevents JavaScript from accessing it.
let a = new Date();
a = new Date(a.getTime() +1000*60*60*24*365);
document.cookie = `mycookie=somevalue; expires=${a.toGMTString()}; Secure; HttpOnly`;
An example you say? No problem. Here I’m creating two cookies, one with httpOnly, and the second without. I’ll try to access the cookie using a script. And what do I receive? Only the cookie without httpOnly:
Not bad, right? And not overly complicated either. This is designed to prevent session hijacking. Cookies are really important for sessions in that they allow us to save a certain connection to a site after log-in. It’s important to remember not to put any critical information on cookies. If I use cookies instead of a session then it’s critical to put only a token and not list a username, and definitely not a password! Since cookies are sent on every request, you can just kinda of ‘set and forget’ it. Once I’ve made them, no malicious script or xss attack can access them.
Hold on though. If we’re using fetch, it’s important to remember that the cookies are not being sent unless we explicitly tell fetch to do so.
fetch('https://example.com', {
credentials: 'include'
})
And that’s exactly what my colleague missed. Since he was looking at cookies as if they’re voodoo, he had some AJAX requests with fetch but was unaware that fetch doesn’t send cookies. In other words, the requests were sent without session causing a 403 error, since the server didn’t recognize it. It cost him a few good hours of debugging. So remember—if you’re executing an AJAX call and get back a 403, open the developer tool and make sure that you’re sending the request like you need to be. If the headers don’t have the cookies, make sure to put in credentials.
About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen.
Recent Stories
Top DiscoverSDK Experts
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment