This is the second part in the series Things I wished I knew when building an authentication system.
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.
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.
There are lots of advertisements about how it prevents modification of data and such. However, in reality, it does not really 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 is 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 this post. It does a good job in the spirit of expressing my frustration.
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, instead 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 cookie).
However, an attacker can manage to include a script in the web application that makes requests on user behalf. A script can be in the form of an image, making it easy to trick the user. 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 special about it is that the image is not visible since its dimension is
0 x 0. 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 include a script that make requests to endpoints. As long as it sets
true. The browser will still include user credential in the request even though it originates from untrusted location.
badscript.js will then make requests to
www.averysecuredapp.com/api/user/delete. The next thing Vic sees are not cast 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.
hen a form is rendered, it is rendered along with a randomly generated token.
<form method="post" action="/api/user/delete"> <input type="hidden" name="csrfToken" value="EFUjo5SBOE2N21m9hOB0AStgVy5iKgsUpCfPDMuSCB6GDlEFKPnVbm8/> <input type="submit" value="Delete my account"/> </form>
When the Vic actually 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's request will not contain the above token and thus be rejected.
Obviously, the CSRF token needs to cryptographically secure. It should not be easy to guess or brute-force.
Every request carries along specific information that tells the server about its origin.
Two common headers to check against are
Referer. With the two headers, the server can see that the request comes from
www.averyattractivelookingsite.com and reject it.
When the server grants the sessionId as 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 append some text to the end of the
Set-Cookie header in response is how server set the cookie when a user signs in. It goes like this:
Setting Samesite attribute will require the server to send this instead.
More information can be found here.
For more information on how to mitigate CSRF, see OWASP guide.
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 makes a search, 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 content:
<p>Search results for <script>hack()</script></p>
<script> tag will be executed by the browser. In this case
hack() is executed.
It is important to sanitize the input
In this case,
<script>hack()</script> will be escaped (Special characters like < > will be replaced). The html of the webpage will now be:
<p>Search results for <script>hack()</script></p>
The browser will not executed
Many frameworks inform developers that they take care of Input Sanitization. We should still be careful nevertheless.
As introduced, cookie is a mean 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 addition protection, also set the
Secure attribute to forces HTTPS connection.
Set-Cookie: sessionId=secret; HttpOnly; Secure;
Read OWASP guide, just do it!
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 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.