Know The Web: HTTP Cookie 🍪
Tags: webdev, httpcookie, javascript, security
Published: 2021-02-17 12:55:21

In this post, we are going to learn about Cookie certainly not the edible one. We'll discuss **cookie properties** and **security stuff** related to HTTP cookies, also **create cookie** on the way so make sure that you and your patience grab milk and cookie, enjoy the post.

## Cookie Time!

While using Facebook, Instagram, or any other services online did you notice that once you logged into these services you don't have to log in when you visit these sites again? 
You searched for shoes and the next moment when you visit any site, you get to see *ads* related to shoes.

Is there some mind-reading stuff going on?

![sorcery](https://media.giphy.com/media/ErHMDXYMfGH96/giphy.gif)

To define, cookies are small chunks of *temporary data* (key-value pair) in the browser which helps in various functionalities in any web services (as mentioned above). These web services/websites setup cookie into your browser and use for features like *managing you session on their service/website*, *to track you* and stuff like that. They can also be used to remember pieces of information that the user previously entered into form fields, such as names, addresses, passwords(not a good idea😅), and payment card numbers.

Now as these websites/web services are able to access the cookie they place in your browser which makes it clear that, *"every time you make a request to the website/web service, the cookie is sent to the server along with the request"*.

## 🕵️‍♂️ Sherlock mode ON!

Let's head over to a random site and have a look at their cookies. On the way, I'll explain about the properties. So I am heading to [motherfuckingwebsite.com](https://motherfuckingwebsite.com/). In developer tools open the **Application tab** and then to **cookie > https://mothe...**.
There you get to see the following:

[![cookies-application.png](https://i.postimg.cc/m2YNcSzM/cookies-application.png)](https://postimg.cc/Xpvymdg7)

Those with *green* underline are options. **Name** & **Value** are self explanatory. The rest are what we need to understand.

- **Domain**

Each cookie has a domain pattern to which it belongs and can only be accessed by that specific domain pattern.

If a cookie named `cookie-1` is added for `.motherfuckingwebsite.com` (notice the `.`) then `cookie-1` can be accessed by any *subdomain* of *motherfuckingwebsite.com*. Example: `cookie-1` can be accessed by the domain `motherfuckingwebsite.com` as well as its subdomain like `www.motherfuckingwebsite.com` or `www2.motherfuckingwebsite.com` and so on.

If a cookie named `cookie-2` is added for a subdomain `xyz.motherfuckingwebsite.com` then it can be only accessed by its subdomain and itself. Example: `cookie-2` can be accessed by subdomain `xyz.motherfuckingwebsite.com` and its subdomain `abc.xyz.motherfuckingwebsite.com` and so on.

you can read more at [RFC2109](https://tools.ietf.org/html/rfc2109)

- **Path**

Suppose you want to make a cookie accessible to a specific path then this option is used. Will explain in a while.

- **Expires/Max-age**

As I have mentioned right in the start that *"cookies are temporary data"* i.e they have a *validity duration* after which they expire. How is *validity duration* determined? By the web service/website. Whenever a website/web service creates a cookie, it also mentions its lifetime.

**HttpOnly**, **Secure** and **SameSite** will be explained in the security section.

Okay! enough talks. Let's create some cookies, heat up your oven (browser)

## 👨‍💻 The Client Way

First we'll discuss about creating cookie from the client-side i.e from the browser using JS which is pretty easy.

`document.cookie`

How about having a look at existing cookie using JS? Just use `document.cookie` in the console and you'll see the following:

[![cookies-console.png](https://i.postimg.cc/0y9G7JD4/cookies-console.png)](https://postimg.cc/JGdBL0T3)

Notice, each cookie is separated by a semicolon(`;`).

- creating simple cookie

```js
document.cookie="itsME=1"
```

> **NOTE**: Above code doesn't override cookies. It only created a new one. 

[![cookie-bake.png](https://i.postimg.cc/MGTyF2sb/cookie-bake.png)](https://postimg.cc/0zTMz31b)

You can see it's defined for domain `motherfuckingwebsite.com` now as per the properties we have discussed above, `www.motherfuckingwebsite.com` should not be able to access the cookie `itsME`.

[![cookie-subdomain.png](https://i.postimg.cc/kM8WZyyZ/cookie-subdomain.png)](https://postimg.cc/HjYJ8XR4)

and we don't see the cookie that we created hence we verified the properties.

- setting up cookie path

How about adding the **Path** option to our cookie? Let's go...

```js
document.cookie="itsMe=7; path=/test";
```
The above code will only set cookie for `motherfuckingwebsite.com/test` and can only be accessed by it. Here is the example.

```js
document.cookie="itsME=7; path=/test";
```

[![cookie-path.png](https://i.postimg.cc/C1nbwCpR/cookie-path.png)](https://postimg.cc/Vr1JRC6w)

*Image 1*: we are accessing cookie from `motherfuckingwebsite.com` and there is no such cookie. 

*Image 2*: we are accessing cookie from `motherfuckingwebsite.com/test` and we can see it.

- Setting cookie lifetime

Let's create a cookie with an expiry date. Now we can do this in two ways.

1. **Expires**: Takes *date* as value.

```js
//86400e3 is same as 86400000 i.e 24 hours in milliseconds
var exp_date=new Date(Date.now()+86400e3);
//refer template literals in JS if not familiar with ${}
document.cookie=`itsME2=2;expires=${exp_date.toGMTString()}`; 
```

2. **Max-age**: Takes time (in *seconds*) as value.

```js
//86400 i.e 24 hours in seconds
document.cookie=`itsME3=3;max-age=86400`; 
```

Above we have created both the cookie with a validity of 24 hrs. from the time the cookie was created. Here you can compare all three cookies we have set so far.

[![cookie-expire.png](https://i.postimg.cc/BQ9BQz64/cookie-expire.png)](https://postimg.cc/QVmTf01y)

Notice! in the **Expires/Max-age** part you can see `ItsME2` and `ItsME3` has some date and time but `ItsME` shows *session*. It is so because when you don't mention any expiry time of the cookie then the browser considers it as a **sessional cookie** and it expires as soon as you close the browser. Go ahead, give it a try.

> 💡 Head over to [didthanoskill.me](http://www.didthanoskill.me/) and look for cookie from the URL bar. You'll see *1 cookie* in use. When you do `document.cookie` in the browser console, an empty string is returned which is weird. Go to the *Application* tab in the developer tool and there also you'll see no cookie. Any idea why is so? *Hint*: have a look at the source and if still don't understand then run the debugger in the dev tool to understand why is it happening so?


## 🖥️ The Server Way

We saw the client's Way of creating cookies. Now we'll create a cookie from the server-side and I'll use **NodeJS** and **express** for this.

Basically what happens is when the client makes a *request* to the server, the server responds with a *response* which contains *header* and in that header, there is `set-cookie` option which tells the browser to create a cookie.

- creating a simple cookie.

```js
const app=require("express")();
app.get("/",(req,res)=>{
    //setting response header
    res.setHeader("set-cookie",["itsSERVER1=h1"]); 
    res.send("this is https://localhost:2000/");
});

app.listen(2000,()=>{
    console.log(">2000<");
})
```
and we have it. 

- setting up cookie path 

```js
const app=require("express")();
app.get("/",(req,res)=>{
    /*can also use res.setHeader() instead of
    res.cookie()*/
    res.cookie("itsSERVER1","h1");
    //for path /hahahayes
    res.cookie("itsSERVER2","yeet!",{path:"/hahahayes"});  
    res.send("this is https://localhost:2000/");
});

app.get("/hahahayes",(req,res)=>{
    res.send("this is https://localhost:2000/hahahayes");
});

app.listen(2000,()=>{
    console.log(">2000<");
});
```

gives following result:

[![cookie-ss-path.png](https://i.postimg.cc/8zwR4Pbz/cookie-ss-path.png)](https://postimg.cc/v4gxHyjJ)

[![cookie-ss-path2.png](https://i.postimg.cc/qqfyqtpj/cookie-ss-path2.png)](https://postimg.cc/ZBcCD5Bd)

so on and so forth for other options as well.

## 🔒 Security

Security is a very important topic of discussion over here. As mentioned earlier, services like social media use various cookies to keep you logged in. If such cookies get in hands of attackers they can easily break into your account and the rest you know.

When user privacy is a concern, it's important that any web app implementation invalidate cookie data after a certain timeout instead of relying on the browser to do it. 

If you are using cookie to store some data and later rendering it in DOM (which is a super duper bad practice) then make sure to keep the valid formatting, they should be escaped using a built-in encodeURIComponent function:

```js
var cookie_name="mycookie";
var cookie_value="myvalue";
document.cookie = `${encodeURIComponent(cookie_name)}=${encodeURIComponent(cookie_value)}`;
```

In section **The Client Way**, we easily accessed the website's cookie using JavaScript, so an attacker may find a vulnerability like [XSS](https://portswigger.net/web-security/cross-site-scripting) which enables them to execute malicious JS code on the website and bypass logins. From a developer's point of view, it's really hard to keep track of XSS especially in humongous applications with a lot of features. Due to this, some inbuilt security features are there in cookies, which prevent such attacks even if the attacker is able to execute some code.

You can check out [Hack this site basic 10](http://souvikinator.netlify.app/blog/hack-this-site-basic-10/) which demonstrates, what careless use of cookies can lead to.

**HttpOnly** is an option used by web-server when they set cookies. This option forbids any JavaScript access to the cookie. This is a precautionary measure, to protect from certain attacks.

```js
//server side
const app=require("express")();
app.get("/",(req,res)=>{
    /*can also use res.setHeader() instead of
    res.cookie()*/
    res.cookie("itsSERVERsecure","100",{httpOnly:true});  
    res.send("this is https://localhost:2000/");
});

app.listen(2000,()=>{
    console.log(">2000<");
});
```
and you'll see a tick mark (✔️) under HttpOnly in the *Application tab* (developer tools). Try accessing it using JS.

If your cookie contain sensitive content then you may wanna send it over **HTTPS**. To accomplish this you have to include **secure** option as shown below.

```js
//client side
document.cookie = "ItsMeSecure=6; secure";
```
```js
//server side
const app=require("express")();
app.get("/",(req,res)=>{
    /*can also use res.setHeader() instead of
    res.cookie()*/
    res.cookie("itsSERVERsecure","100",{secure:true});  
    res.send("this is https://localhost:2000/");
});

app.listen(2000,()=>{
    console.log(">2000<");
});
```

**samesite** SameSite prevents the browser from sending the cookie along with cross-site requests. Possible values are *lax*, *strict* or *none*.

The **lax** value will send the cookie for all same-site requests and top-level navigation GET requests. This is sufficient for user tracking, but it will prevent many [CSRF attacks](https://portswigger.net/web-security/csrf). This is the default value in modern browsers.

The **strict** value will prevent the cookie from being sent by the browser to the target site in all *cross-site browsing* contexts, even when following a regular link.

The *none* value explicitly states no restrictions will be applied. The cookie will be sent in all requests—both cross-site and same-site.

So make sure that you use cookies wisely 🦉. 
Feel free to point out any issues or suggest improvements in the content.

🥳 So it's time to wrap up the post with a quote 

> "Opportunities don't happen. You create them" -Chris Grosser