Great authentication systems come with great responsibilities. If I am going to implement my authentication system, it needs to be secure.
This is the second part in the series Things I wished I knew when building an authentication system.
Implementing security practices
As I said, you need to be responsible for the security of the authentication system. You should not write the system and hope that it does not go wrong.
One of the best resources I can refer to is the Open Web Application Security Project (OWASP).
There are a lot of security measures to consider when designing an authentication system. I will include some of the most famous ones.
Some myths when it comes to security
HTTPS does not mitigate attacks
Although it is helpful to have HTTPS on your website to encrypt data transmission. It can indeed help protect users from various attacks such as man-in-the-middle, where the data transmission is intercepted and modified.
JSON Web Token, so hot right now
There are lots of advertisements about how it prevents modification of data and such. However, in reality, it does not protect the user from everything.
Furthermore, it is also very misunderstood. If you look up "How to create an authentication system", almost half of them are about "How to use JSON Web Token for authentication".
I see most of them set the token in the cookie. It is not how it goes!!! Some even inject every piece of data into the token. What if the token gets leaked? Your users' information will be in the hands of bad people.
I recommend reading the post Stop using JWT for sessions. It does a good job in the spirit of expressing my frustration.
Prevent CSRF attacks
Cross-Site Request Forgery (CSRF) is a type of attack that makes use of the user session.
In the old days, every time a user wants to do something, he or she has to reenter the password, which is very inconvenient. To tackle this problem, a session is established when the user to login to the website so that he or she does not have to sign in again. While the session is active, the user can make changes to their account by making requests to endpoints while attaching the session token (usually in the form of cookies).
However, an attacker can manage to include a script in the web application that makes requests on the user's behalf. A malicious script can be in the form of an image, making it easy to trick the user into executing it. For example, the attacker can create a post on
www.verysecuredapp.com/forum/post/averycutecat. Vic, who loves cat, go to see the post. What Vic does not know is that the post includes an image:
<img height="0" width="0" src="www.badboyx123.com/badscript.js" />
What is special about it is that the image is not visible since its dimension is 0px × 0px. The browser will try to load everything on the page. It will load
badscript.js even though it is included as an image.
It does not necessary for the script to be included as an image. Request can be made cross-origin. For example, the attacker can trick the user into visiting
www.averyattractivelookingsite.com, which includes a script that makes requests to endpoints. As long as it sets
true, the browser will still include user credentials in the request even though it originates from an untrusted location.
badscript.js will then make requests to
www.averysecuredapp.com/api/user/delete. The next thing Vic sees is not a cat but his account deletion.
One way to prevent CSRF is to implement a security token. In every form, a CSRF token is attached as a hidden field.
When a form is rendered, it is rendered along with a randomly generated token.
<form method="post" action="/api/user/delete"><inputtype="hidden"name="csrfToken"value="EFUjo5SBOE2N21m9hOB0AStgVy5iKgsUpCfPDMuSCB6GDlEFKPnVbm8"/><input type="submit" value="Delete my account" /></form>
When Vic wants to delete his account, he will click the button in the above form. The
csrfToken is sent along with the request, which will be verified when it reaches the server.
badscript.js request will not contain the above token and thus be rejected.
The CSRF token needs to be cryptographically secure. It should not be easy to guess or brute force.
Verifying origin with standard headers
Every request carries along with specific information that tells the server about its origin.
Two common headers to check against are Origin and Referer. With the two headers, the server can see that the request comes from
www.averyattractivelookingsite.com and reject it.
Samesite Cookie Attribute
When the server grants the sessionId as a cookie, it may set the Samesite attribute. This attribute forces the browser to reject sending along with the cookie if the request is not originated from the trusted domain.
This requires the Cookie to be configured properly. Doing so is as easy as appending some text to the end of the Set-Cookie header. Set-Cookie header in response is how a server sets the cookie when a user signs in. It goes like this:
Setting Samesite attribute will require the server to send this instead.
For more information on how to mitigate CSRF, see OWASP guide.
Cross-site scripting (XSS)
Cross-site scripting (XSS) is very similar to CSRF. It describes the method of including malicious scripts on vulnerable websites.
If your website has a blog, chances are it has a search bar. When the user searches, he/she will be redirected to an URL like the one below:
On the destination, users should see something like:
Search results for "Picture Of Cats"
Its HTML is:
<p>Search results for Picture Of Cats</p>
Picture Of Cats was copied from the query in the URL!
What if an attacker crafts an URL:
and somehow trick the user to click on it.
He or she will be shown a page with the following HTML:
<p>Search results for<script>hack();</script></p>
<script> tag will be executed by the browser. In this case
hack() is executed.
Sanitize the input
It is important to sanitize the input
In this case,
<script>hack()</script> must be escaped (Special characters like
< > will be replaced). The html of the webpage will now be:
<p>Search results for <script>hack()</script></p>
With the above, the browser will not executed
Do not rely on the framework
Many frameworks inform developers that they take care of Input Sanitization. We should still be careful nevertheless.
As introduced, a cookie is a means to store a user's session. If the attacker can get access to the cookie, he or she can hijack the user's session. A malicious script can call document.cookie to retrieve the cookie.
HttpOnly attribute forces the browser to make the cookie inaccessible from client-side scripts. Doing so by:
Set-Cookie: sessionId=secret; HttpOnly;
As an added protection, also set the Secure attribute to force HTTPS connections, which prevent man-in-the-middle attacks.
Set-Cookie: sessionId=secret; HttpOnly; Secure;
Endnotes on security
Read OWASP guide, just do it! https://www.owasp.org
Security is not simple seriously. Refer to the guide to learn how to secure your app.
With the above introductions, I hope I can get you started to write your authentication system. It may take a lot of effort into implementing your own, and it is alright to go with a third-party service. I personally want to challenge myself and create my own. However, if I use it in production, I will need to work a lot on making sure that it is safe and convenient for my users.