Hello World! Web development is one of the most popular fields nowadays and we can see every day new developers are joining this field because of high-paying jobs. Javascript is the most common and loved language among developers because it offers a lot of frameworks available to make the development process look like a piece of cake and its ecosystem of course.
In my past working with different teams, I have realized that developers (sometimes including me, I am not the perfect guy) do not take the most crucial processes of authentication seriously and because of this, it leaves security vulnerabilities in the application.
What is Authentication Anyway?
Let's understand with a very obvious example what does authentication means in the web application. Suppose you are going to the nightclub to have a beer and the person in charge ask for your identity to confirm your age which is required to enter. Now if you have this and the age is above that of what the government has decided, you are "allowed" to enter the club and you will be stamped on your hand. Whenever you are supposed to "prove your identity" to another party, the process is known as authentication.
How does authentication differ from authorisation?
I see people often confused with authentication and authorisation. Well at some point I also got confused but later it seemed a thin line between these two. Let's take the same club example and explore this.
Suppose you are willing to go inside the club and explore its resources, but you forgot to carry the identity card. In that case, you have no right or access to its infrastructure. One can also say that "Hey you are not authorised to enter the club". Or even though you have passed the first process of authentication, but still there are some areas where only the staff of the club are allowed to enter (like the kitchen or behind the bartender table). So whenever "right to access" comes into the frame, you are talking about authorisation.
If we see from the top, the authentication is also a form of authorisation which will give you right to access the resources that you are initially not allowed to.
Understanding Cookies in Detail
The session is stored on the server-side and the public identity of the session is saved on the client side every time there is a successful call to the login route. The server instructs the client by sending the Set-Cookie
header in the response with some extra data that instruct the browser on how to store this information.
Let's try sending an authentication request to a live server and see how it responds.
curl 'https://authenticationtest.com//login/?mode=simpleFormAuth' \
--data-raw 'email=simpleForm%40authenticationtest.com&password=pa%24%24w0rd' \
--compressed -i
Well as you can see in the response I have the cookie name PHPSESSID
and its value 2ifo....
.
Once this cookie is set successfully on the browser storage, it is automatically mapped with the current domain name (here authenticationtest.com). So whenever the request will be made to the resources on this domain, the browser will automatically include the cookie name and its value in the Cookie
header which will be then used by these resources to identify the user.
Note: Cookies are also used by tracking services like google ads, Facebook pixel and etc. So when you enable anti-trackers on the browser, they simply disallow the browser setting these cookies.
Setting the Expiration Configuration
The Max-Age
option in the Set-Cookie header tells the browser after how many seconds automatically expire this cookie and delete it from its storage. This is optional because there is also another option by setting the Expires
option which accepts the UTC format of date and time exactly when to expire the cookies on the client-side.
When the Max-Age is zero or a negative number, it will expire the cookie immediately after storing and when the Expires option is present along with this, the Max-Age has more precedence.
Some of the examples of the Set-Cookie header with this configuration are shown below
Set-Cookie: MYCOOKIE=Hello World; Max-Age=60 // This will be expired after 1 minute
Set-Cookie: MYCOOKIE=Hello World; Max-Age=-1 // This will be expired immediately
Set-Cookie: MYCOOKIE=Hello World; Max-Age=0
Set-Cookie: MYCOOKIE=Hello World; Expires=Wed, 1 Mar 2023 00:00:00 UTC // This will be expired on 1st march 2023 UTC time
Set-Cookie: MYCOOKIE=Hello World; Max-Age=60; Expires=Mon, 26 Mar 2018 17:04:05 UTC // The Max-Age configuration will use and the cookie will be expired in 1 minute
Sending the Cookie on a Specific Document Path
Suppose you have two cookies, one is supposed to send on all the requests and another is to be sent on a specific document. How would you control this since they are automatically sent by the browsers? This feature is implemented in the Path
option of the Set-Cookie header.
When the path is only /
, the cookie will be sent for all the requests, but when the valid path exists in the option, it will be only sent if the document location matches with the path in the form of "starts with" regex. Let me explain to you with an example – Suppose the path of the cookie is set to the /secret, then this cookie will be sent for all the resources starting with "/secret", including /secret/, /secret/doc, /secret/doc/ and so on.
Some of the examples are shown below
Set-Cookie: MYCOOKIE=Hello World; Path=/ // This will be sent on all the resource requests
Set-Cookie: MYCOOKIE=Hello World; Path=/secret // This will be sent on all the resource requests whose document location starts with /secret
This option can be used to provide the special permissions (authorization) on the specific path to the specific users.
Sending the Cookies on Secure Connection
When the request is made over a secure connection such as an HTTPS connection whether the network packets are signed by a secure hashing function a certificate, it becomes hard for the attacker to perform a man-in-the-middle attack to read or tamper with the request.
Therefore to provide security from MITM attacks in the network, the server can instruct the browser to send the cookie only if the request is made in a secure environment under HTTPS. This can be done by providing Secure
option in the Set-Cookie header as demonstrated below
Set-Cookie: MYCOOKIE=Hello World // This will be sent on requests made in both Http and Https connection
Set-Cookie: MYCOOKIE=Hello World; Secure // This will be sent only on requests made in Https connection
Allowing Only HTTP Requests to Read Cookies
If somehow the attackers get control to execute arbitrary javascript commands on the website, they can get the cookies using document.cookie
and which contains your proof of identity of the website and use it to perform actions on your behalf.
Need not worry, the browser got you covered. You can set a HttpOnly
flag in the Set-Cookie header of the authentication response which will indicate the browser to only use this cookie when performing HTTP requests. You can still see the cookies via the developer console window, but not via javascript.
Set-Cookie: MYCOOKIE=Hello World; // This can be accessed by document.cookie API
Set-Cookie: MYCOOKIE=Hello World; HttpOnly // This can only be accessed while making http connections
Set-Cookie: MYCOOKIE=Hello World; HttpOnly; Secure // This can only be accessed while making secure http (https) connections
Best practices for the developers
There are a few best practices that I use and recommend to you if you are new to the javascript world
- The users do not log out of the application and the sessions are kept on the login state. Make sure that there should be an expiration for the cookies or tokens. This will prevent the infinite login time for the user reminding them to log in to the applications. I would recommend you having 7 - 14 days of expiration time
- The users often rely on the external password manager like 1Password or browser built-in features. This helps them to use strong passwords without remembering all of them. The forms with proper names and inputs should be used so that password managers recognise them and automatically save and fill them. This will remove time to enter the password which can be seen by someone (kind of eavesdropping)
- When the user clicks on the logout button, the backend request should be made to invalidate cookies, tokens. This is a step ahead of security measures for the first point. Just in the case through XSS the cookies are stolen, it should be invalidated in the backend (associated session) so that it now becomes useless for the attackers.
- Even if you think the information is not as sensitive, always encrypt the cookies to a specific algorithm and same as decryption logic in the server. I would rather recommend you use a hashing algorithm and associate the user id with it on the sessions table. When the cookie is present in the protected resources, try to get the user from user id associated with that cookie hash.
- Apply rate-limiting to the requests brute-forcing cookie header to avoid accidentally guessing the correct cookie for a user on your application.
- Use of
HttpOnly
+Secure
cookies are 10 times more secure than other cookies as it allows the browser to send the cookies only when the HTTP connection is made to that domain and document and secure will ensure that the protocol is secured by TLS so that there is no chance of MITM attack
References
- https://null-byte.wonderhowto.com/how-to/write-xss-cookie-stealer-javascript-steal-passwords-0180833/
- https://book.hacktricks.xyz/pentesting-web/hacking-with-cookies
- https://salferrarello.com/httponly-cookies
- http://blog.k3170makan.com/2013/10/aboutme-cookie-based-xss.html
- http://blog.miladbr.ir/2013/04/exploiting-unexploitable-dom-based-xss.html
Top comments (1)
Thanks so much! I finally understood the diff between authentication and authorization. Gotta implement those tips on my projects as well!