Full guide to secure cookies in JavaScript (client-side)

cookies and JavaScript

Tags: javascript, cookie, js, security

What are Cookies?

HTTP cookies (also called web cookies, Internet cookies, browser cookies, or simply cookies) are data, stored in small text files, on your computer.

When a web server has sent a web page to a browser, the connection is shut down, and the server forgets everything about the user.

Cookies were invented to solve the problem "how to remember information about the user":

  • When a user visits a web page, his/her name can be stored in a cookie.

  • Next time the user visits the page, the cookie "remembers" his/her name.

Cookies are saved in name-value pairs like

1token = 123

When a browser requests a web page from a server, cookies belonging to the page are added to the request. This way the server gets the necessary data to "remember" information about users.

None of the examples below will work if your browser has local cookies support turned off.

JavaScript can create, read, and delete cookies with the [ "document.cookie" ] property.

Create a Cookie with JavaScript

With JavaScript, a cookie can be created like this:

1document.cookie = 'token=123';

Persistent cookie

A persistent cookie expires at a specific date or after a specific length of time. For the persistent cookie's lifespan set by its creator, its information will be transmitted to the server every time the user visits the website that it belongs to, or every time the user views a resource belonging to that website from another website (such as an advertisement).

For this reason, persistent cookies are sometimes referred to as tracking cookies because they can be used by advertisers to record information about a user's web browsing habits over an extended period of time. However, they are also used for "legitimate" reasons (such as keeping users logged into their accounts on websites, to avoid re-entering login credentials at every visit).

Example:

1document.cookie = 'token=123; expires=Thu, 27 Dec 2021 12:00:00 UTC';

Alternatively, the Max-Age attribute can be used to set the cookie's expiration as an interval of seconds in the future, relative to the time the browser received the cookie.

Session cookie

A session cookie (also known as an in-memory cookie, transient cookie, or non-persistent cookie) exists only in temporary memory while the user navigates a website. Session cookies expire or are deleted when the user closes the web browser. Session cookies are identified by the browser by the absence of an expiration date assigned to them.

With a path parameter, you can tell the browser what path the cookie belongs to. By default, the cookie belongs to the current page.

Just set it with no specified date

1document.cookie = 'token=123;';

Domain and Path

The Domain and Path attributes define the scope of the cookie. They tell the browser what website the cookie belongs to. For security reasons it can only be set and accessed by the top domain (If not specified then the current domain it's been set on, and the path is / by default) and its subdomain so site example1.com can't access the cookie of example-2.org

1document.cookie = 'token=123; expires=Thu, 27 Dec 2021 12:00:00 UTC; path=/';

Secure and HttpOnly

The Secure and HttpOnly attributes do not have associated values. Rather, the presence of just their attribute names indicates that their behaviors should be enabled.

The Secure attribute is meant to keep communication limited to encrypted transmission, directing the browser to use cookies only via encrypted/secure connection.

However if a web server sets a cookie with Secure from a non-secure connection it can still be intercepted by a man in the middle attack, so consider setting them via secure connection only for more security.

The HttpOnly attribute directs the browser to not expose cookies through channels except for the HTTP/HTTPS request.

The HttpOnly will lead to the cookie not be accessed by client-side scripting but it can be accessed the server via the HTTP/HTTPS request.

Same-site

SameSite can have a value of Strict, Lax or None.

  • SameSite=Strict the browsers would only send cookies to a target domain that is the same as the origin domain. This would effectively mitigate cross-site request forgery (CSRF) attacks.

  • SameSite=Lax browsers would send cookies with requests to a target domain even it is different from the origin domain, but only for safe requests such as GET (POST is unsafe) and not third-party cookies (inside iframe).

  • SameSite=None would allow third-party (cross-site) cookies, however, most browsers require secure attribute on SameSite=None cookies.

Example:

1document.cookie = 2 'token=123; expires=Thu, 27 Dec 2021 12:00:00 UTC; path=/; SameSite=Lax';

Let's Create a function that will Create the cookie

1export const setCookie = (cookieName, cookieValue, options = {}) => { 2 let cookieString = ''; 3 const { 4 expiryDate, 5 domain, 6 path = '/', 7 samesite = 'Lax', 8 Secure = true, 9 HttpOnly, 10 } = options; 11 12 cookieString += `${cookieName}=${cookieValue || ''};`; 13 if (expiryDate) 14 cookieString += `Expires=${new Date(expiryDate).toUTCString()};`; 15 if (domain) cookieString += `Domain=${domain};`; 16 if (path) cookieString += `Path=${path};`; 17 if (samesite) cookieString += `samesite=${samesite};`; 18 if (HttpOnly) cookieString += 'HttpOnly;'; 19 if (Secure) cookieString += 'Secure;'; 20 21 document.cookie = cookieString; 22};
1// Lets's set a token = 12345 to cookie 2const expiryDate = new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 10); // 10 days 3setCookie('token', '12345', { 4 expiryDate, 5 // ... other options you want to set 6}); 7// We will set token_expiry_date to cookie since we will need it later 8setCookie('token_expiry_date', expiryDate, { 9 expiryDate, 10 // ... other options you want to set 11});

Read a Cookie with JavaScript

document.cookie will return all cookies in one string much like: cookie1=value; cookie2=value; cookie3=value;

Let's Create a function that will read the value of the name we want in a cookie string

1const getCookie = (cookieName = '', cookieString = document.cookie) => { 2 const decodedCookie = decodeURIComponent(cookieString); 3 const cookieArray = decodedCookie.split(';'); 4 let cookieValue; 5 6 cookieArray.find((cookie) => { 7 if (cookie.trim().startsWith(`${cookieName}=`)) { 8 cookieValue = cookie.substr(cookie.indexOf('=') + 1); 9 return true; 10 } 11 }); 12 13 return cookieValue; 14};

Change or update a Cookie with JavaScript

With JavaScript, you can change a cookie the same way you create it

  • The bad thing is the options will be overwritten including expiry date and if no value is provided it will turn it into a session cookie which will delete itself when you leave, We can use token_expiry_date that we set in the cookie earlier in a cookie to work around this issue!
1// let's take expiryDate from the cookie that we set previously first 2const expiryDate = getCookie('token_expiry_date'); 3 4// Then let's change the token value in the cookie 5setCookie('token', '54321', { 6 expiryDate, 7 // ... Any other options you have previously set and don't want to change it 8});

Delete a Cookie with JavaScript

Deleting a cookie is very simple.

You don't have to specify a cookie value when you delete a cookie.

Just set the expires parameter to a past date:

If you stored its expiry date in the cookie like I did don't forget to delete it.

1setCookie('token', undefined, { 2 expiryDate: 'Thu, 01 Jan 1970 00:00:00 UTC', 3}); 4// Don't forget 5setCookie('token_expiry_date', undefined, { 6 expiryDate: 'Thu, 01 Jan 1970 00:00:00 UTC', 7});

Or Mke a function for it:

1const deleteCookie = (cookieName, options = {}) => { 2 // We can 3 document.cookie = `${cookieName}=''; Expires=Thu, 01 Jan 1970 00:00:00 UTC; Path=${ 4 options.path ? options.path : '/' 5 }; ${options.domin ? `Domin=${options.domin}` : '/'};`; 6 // Or 7 setCookie('token', undefined, { 8 expiryDate: 'Thu, 01 Jan 1970 00:00:00 UTC', 9 ...options, // Like the path 10 }); 11};
1deleteCookie('token'); 2deleteCookie('token_expiry_date');