Bulletproofing your web-services security
Building top-tier software is a must at eDreams ODIGEO (eDO henceforth). Thus, in our strive to reach the highest quality for end-users, we try to apply top standards in our product. This effort is coal-and-ice in every single area of our development process, but there is a special section where it stands out even more, if possible: Our user’s privacy. Ensuring an absolutely secure end-to-end communication is one of the keys in our success. Therefore, building robust web-services is mandatory. The present article covers a way of making a resilient and secure web-service for login users.
How does a simple login process look like?
The following picture depicts a simple, functional login process for a web application:
Figure I: Sequence Diagram for a simple login process
Accordingly, username and password are retrieved in the web-browser, then forwarded to the web-service, validated there and forwarded and saved at the Persistence Layer.
In spite of its easy appearance, a gargantuan complexity lies behind this diagram:
– What would happen if an attacker obtains access to Persistence Layer?
– How can we safely store critical data?
– How can we obscure this data?
– How can we hide the highest possible amount of info?
Unfortunately, this problem does not end when these questions are solved. These questions apply from Client-to-Server communication, but surely we need to ensure data safety for the other way around, too.
Bulletproofing the login process
Hide as much info as you can
As the wise old-saying states, “It takes two to tango”. Let Batman and Alfred be those two.
Batman (AKA Client) is on duty tonight and wants to know the list of villains spreading mayhem around Gotham, so he commands Alfred (AKA Server) to send the list containing them. Alfred, a wise man, has created a web-service for this duty with the following endpoints:
BAT-URL | HTTP METHOD |
https://<bat-url>/user/create | POST |
https://<bat-url>/user/login | POST |
https://<bat-url>/getEnemies | GET |
https://<bat-url>/logout | GET |
Important to note: Alfred is never allowing any value inside the URL, i.e:
https://<bat-url>/user/create/{name}/{password} |
https://<bat-url>/user/login/{name}/{password} |
Using HTTP POST, these values will be encapsulated in the body, not in the header, therefore they’ll be hidden.
Besides, a brute force attack trying to hit those URLs won’t work so easily with POST.
Never trust a single layer of encryption or hashing
Alfred, an avid reader, reminds us about an important sentence about security:
“The only truly secure system is one that is powered off, cast in a block of concrete and sealed in a lead-lined room with armed guards.”
– Gene Spafford
Thus, any additional security layer is never enough. (Though a logic threshold needs to be applied at certain point.) So, at this point, Batman can register himself by hitting:
https://<bat-url>/user/create |
POSTMan (https://www.getpostman.com/) is an awesome tool that allows him to do so:
Figure II: User creation by means of REST API access through POSTMan
It won’t be implemented here for clearness purposes, but please note that an additional security layer will be needed here in production environments:
On the one hand, a timer can be triggered after each request, with a proper interval pursuing the avoidance of DDos (Distributed Denial of Service) attacks.
On the other hand, the same HTTP Status can be returned independently of the actual success of the operation. This will obscure the result just for the eyes of an attacker.
For the sake of clarity, these aspects will be taken for granted during the current article.
So, how can Alfred safely save user’s data? One of the possible actions he can take is to apply Salt encryption technique, where salt represents a random string used to obscure the real data. This mixture can be hashed with a function (SHA-512 in our case) in order to reach a salted hash value:
Figure III: Getting a Salted Hash Value – Conceptual Scheme
To understand the difference between cracking a single password and a set of them, consider the example of a single password file that contains hundreds of usernames and hashed passwords.
Without a salt, an attacker could compute hash(attempt[0]), and then check whether that hash appears anywhere in the file. The likelihood of a match, i.e. cracking one of the passwords with that attempt, increases with the number of passwords in the file.
However, if salts are present, then the attacker would have to compute hash(salt[a], attempt[0]), compare against entry A, then hash(salt[b], attempt[0]), compare against entry B, and so on. This defeats reusing hashes in attempts to crack multiple passwords.
Thus, after receiving https://<bat-url>/user/create , Alfred will generate a random salt and store three elements:
1) username
2) salt
3) Let SHA-512(string, salt) be a function returning the string salted and hashed with SHA-512, the result of the following recursive call will be stored:
SHA-512 (SHA-512(password + salt).concat(salt), salt)
Thus, the saved password for the POSTMan example will be:
1ad12e6a457f92d5b8ec38516c04774e3c963f32d034cc5bd2d3e54a82590a7 646c44c35155f9a4e3117ff23157cefa2f8294a6ae4ad8e27a99bc9a435730ea7 |
At this moment, Batman can log himself in:
Figure IV: User login by means of REST API access through POSTMan
Always save an ace up your sleeve
The Joker wants to know if he’ll be confronting Batman soon, so he tries to access:
https://<bat-url>/getEnemies |
Two scenarios may appear here:
1) The design does not scan this flaw and The Joker takes one step further towards Batman 🙁
2) The system knows if Batman is logged in, stopping The Joker from retrieving the information (and it does! 🙂 ):
Figure V: Security sub-system preventing information leaking through Predictable Resource Location attacks
There are many valid ways of implementing this approach, but the key point here is to cover yourself with a fallback if everything else fails. No need to remember that quote from Mr. Spafford about the only truly secure system, right? 😉
Security never ends
So, Batman has created his user and is logged in, so, he wants to retrieve every enemy he has to deal with from Alfred’s system.
Then, Alfred can send the plain list containing the enemies, right? RIGHT?
A true, strictly trained by Ra’s al Ghul, warrior from the League of Shadows knows that he can’t:
-What if The Joker retrieves the plain list whilst its traveling over a network?
-What if The Joker not only retrieves the list, but changes it (which is even worse) and re-sends it?
-How can Batman be sure that it’s from Alfred’s?
JWT (JSON Web Tokens – https://www.jwt.io/) is your friend here. JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties, as it is the case.
Thus, instead of sending a plain, crystal-clear list of enemies, this is what Batman will retrieve from Alfred:
Figure VI: Retrieving encapsulated JWT information after valid login
OK, but, how can Batman know what is the meaning of this message?:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJpc3MiOiJBbGZyZWQgSi 4gUGVubnl3b3J0aCIsImVuZW1pZXMiOiJUaGUgSm9rZXIsIEJhbmUsIENhd HdvbWFuLCBEZWF0aFN0cm9rZSwgS2lsbGVyY3JvYywgVHdvLUZhY2UifQ.J D4K9FH_HBbQQp2u6B2BWE0amt87bbHBM67Qhs8mO2E |
Simple, he just needs to decode it according RFC 7519. This can be as easy as copy-pasting the message in https://www.jwt.io website:
Figure VII: Plain-Text payload belonging to the sent JWT token
Note that Batman is not only able to know that the issuer is “Alfred J. Pennyworth” (a trusted source), but which enemies he must pursue as well.
Please note that an additional layer of security can be used here by encrypting the JWT token (for instance, following a RSA Cryptosystem Scheme with a pair of public/private keys for Batman and another one for Alfred), but it hadn’t been included in this article chasing more legibility.
Conclusions (TL; DR)
Getting fully operative web-services is not a problematic task, but secure and resilient services is, as well as a single point of failure. Therefore, information shared inside signed JWT tokens, reinforced with encryption techniques such as Salt engagement and hashing methods like SHA-512, can constitute the bulletproof vest you need for building unyielding web-services.
Note: You can find the source code here. The next article will focus on how to properly unit-test these web services, so stay tuned! 🙂
Great Post Clemen
Dark knight you are in the mud!
Thank you for your feedback! I’m glad you liked it! 🙂