Welcome to my blog, this blog post contains the guide of how we can secure the REST API from security vulnerabilities if left open publically. I have seen many websites were I found lot of open APIs without having security machenism, and those leads us to IDORs, PII information leaking, and others. So what primary steps we should take while securing REST APIs. This blog post assumes that you know the REST API and its basis, or you will get an idea while we proceed further.
Most common attacks that happens in APIs, (REST)
1. Lack of authentication.
2. Sensitive internal information
4. Dangerous methods left open like DELETE
5. Validate the user input
We will see how we can over come from all these one by one with practicle example. One note point is, we all do this in **Python3** using Flask.
Note:1: The context of this blog post is, 'How you should create your API so that if left unintentionally open publicaly, it doesn't affect to the security'.
Note:2: You can find all the code used on this vulnerable Flask app here.
In a service, there will be a number of APIs which are playing different kinds of role, so it becomes very difficult to implement authentication on each and every API. For ex: I was working on a popular website, where I was able to get the route paths, which contains a huge number of API endpoints so I cheked some of the APIs and those were secured, they asked for token, but as there are lot of APIs so I quickly wrote a script to find out which API is vulnerable for lack of authentication, and I found an API which leaks everything of their customer like, Customer name, PAN card, Mobile Number, Room Number, Cash receipts, Transaction ID, Photo, Adhar Number, etc And that was leaking for huge number of customers.
So this might give you an idea, what exactly I am talking about. Below is the code, you can see, we were able to secure the GET method, but not delete. And the consequences are, we can successfully delete the user.
Let me just explain a bit about this code, we first create a `require_auth` function in which we are wrapping (@wrap) the function 'f' or whaterver function we provide. Here, we are wrapping `get()` by using `requires_auth` decorator inside the class. So in short what exactly happens is, when we call `GET` request on this API `/api/v1/users/[id]`, it first calls checker function , then `checker` function check if header `Authorization` is not present return 401, and if it is present then it checks the token with the actual token in the backend. If token matches, checker returns
f function basically,
get function and then
get functions gets executed.
So we check if this code works exactly that we think it should.
When we provide invalid access token(Check the Authorization header), we were able to get the 401 error.
When we provide valid token
As you can see, we didn't wrap `delete` function, so that means we can use DELETE method to delete the user.
So this it is very important to check each function is secure or not, in this case, what we need is, we need @require_auth decorator on the function which we wanted to secure, just a single line and you can save your function from web attack. So adding require_auth decorator on each function, then we are free even if this API have public accessed intentionally or unintentionally.
Okay, let's suppose, we are not securing any function here neither GET nor DELETE (Meaning we are not wrapping any function)?, what the attacker can do here?
I have seen many times that, by access the path, we got lot of sensitive data, by just browsing the API. Some of the examples are:-~ /status
There are tons of Path. So this weakness also a part of Lack of Authentication, the only best way I can imaging is, enforing the authentication as decribed above.
IDOR is a kind of vulnerability through which attacker can change its (object) reference to retrieve information of others. Context of this attackers differ on the basis of the functionality in the website, here, we are retreiving information by ID.
Ex:1- Look at the below example
This API retrieves the users information by looking at the id after the users path. i.e. 110. Now this id you can see is guessable, what if I try change it to 109, if the server is vulnerable to IDOR, it will change the reference to retrieve other users information.
Similarly we can emuerate all the users, so this is just a simple IDOR vulnerable environment
So did you get the root cause? I would say because of the ID was guessable, it was possible to get other users information. I am not saying that the website is not validating session with user id, that is the different case, here we are assuming that we left REST API open publiclly.
You can also check the source code, you will find that first time we create the user using POST request, we create it as it is without hashing anything.
Then what should we do?
If we can make user id impossible to guess it? Would it solve our problem? Let's find out.
Now you can see, in the first time we create the user we encrypt the userid as hash_id(id), because user id was the primary key through which the user can retrieve his/her data. Now what he will get his user id is:-
This URL gives us this:-
Now how will you guess other Users ID? Would it be possible? I don't think so. So we concluded that, if your rest API opens publically, the user can not enumerate other users data if the primary key is unguessable? Would it be possible?.
Yes, if the Websites are using UID in URL, the uid can get leaked in third party websites, but for that, the attacker now needs other vulnerabilities help. Vulnerabilities like XSS. But you have made this attack very complex, with less severity by just Hashing the UID.
Methods are GET, POST, PUT, DELETE, OPTIONS but we are talking about the dangerous method DELETE. What if we allowed DELETE method?, We serously can delete anyone user from the SYSTEM
URL: https://4e686f4297a3.ngrok.io/api/v1/users/57c2ebe70049f224e5756afbb3d8526d3205a27999be7062d6680d020d7f1bc3 with
cmd: ` curl -XDELETE --no-ssl "https://4e686f4297a3.ngrok.io/api/v1/users/57c2ebe70049f224e5756afbb3d8526d3205a27999be7062d6680d020d7f1bc3"
But if you hashed the primary key ( Through which the server delete the set from Database, in this case UID.) this problem is also solved. But because its DELETE, the severity is high, because if the attcker can find out the UID some how by exploiting XSS, etc, he/she can delete him/her. But why do you need DELETE method? I would say, we should definetly not.
If the API is missing the auth token, but still the API must look into the CSRF token header, just to prevent from CSRF attacks.
Validating user input is another important task in securing API, suppose, if you are accepting user email without validating, another example can be a SQL Injection
So the implementation should be like, you first receives the request, then you retrieves all the parameter name and values and then you send those parameters to your validation method which will validate each parameters:-
Please ignore bad code here,, check_validation method will validate according to the parameter context, if it is int, validate it in INT context, if it is email, validate it in EMAIL context, so on..
Thanks for your time, hope you find it useful somehow, also please let me know if I did some mistake. Contact me on twitter @agrawalsmart7