Building securing APIs
In the age of data breaches & everything digital, there was never a lesser need to build secure APIs. Insecure APIs means:
Loss of private data
Financial Loss
Legal Implications
and many more.
Enough reasons to bring down a company. Let's see how to secure an API. I'll be using a simple use case and show you a few scenarios of how attackers think. At the end of this post, I also share my advice on simple ways to secure an API. Grab a coffee and read on.
Let's take a simple example
Assume you are working on an API that allows the user to log in and upon success, the backend provides a JWT token for the user for further authorization. Let us assume the user can add, modify and delete a project. Under each project, the user can add tasks.
A typical project could be like "I want to learn to drive". Tasks could be to "contact a friend to accompany" and "contact a learning school" etc.
Assume below are some end points you are having:
localhost:9000/api/user/password-reset
- It is also ok to use localhost:9000/api/user/password/reset
localhost:9000/api/project --> Create a project (POST)
localhost:9000/api/project{projectId} --> Delete a project (DELETE)
localhost:9000/api/project{projectId}/task --> Create a task under a project (POST)
localhost:9000/api/project{id}/task/{taskId} --> Update or delete a task under a project (Put/Patch or Delete)
localhost:9000/api/project{id}/search?name=something&date=something --> Search for a task under a project (GET)
The uniuq id of the user who is submitting the requests & his/her token will be sent to the server via the HTTP headers.
Given this simple API, How do you go about securing your API? For that, you need to understand the "areas" where an attacker would fancy a chance. Let's see what are the attacking points given a rest endpoint:
URL Path.
User Token.
Payload.
Query Strings.
Here we go.
Let's start by first logging into the application and grabbing the token. The token is either stored in a cookie, local storage or session storage. ( If you are not sure how to do it, ask a fellow developer. Take her/him to a coffee later)
Once you have the token, open Postman, start hitting the APIs and make it work - The regular flow (like how your UI calls). Once it's working and you understand how Postman works, let's start working on the actual testing which shows how secure our APIs are.
Data Setup
Here are some assumptions we will make (Just for this example). We will have two users ( User A and User B) who can create projects and tasks. All projects created by User A will start with the alphabet A only and User B projects will start with the alphabet B.
Given these, let's see some examples of negative tests or white-box testing.
Scenario 1
Consider this URL to delete a project: http://localhost:9000/api/project/{projectId}
The real-world URL could be like this: http://localhost:9000/api/project/1
or http://localhost:9000/api/project/09abcf21
What to do?
Now try to change the {projectId} as suggested below and hit the API as User A:
Provide an Invalid projectId
Blank/Empty projectId
Valid projectId but the projectId belongs to User B.
Worse Case: You get a not-so-user-friendly error for the first two points. The third request deleted the project belonging to User B.
Best Case: Your server gives you a "Bad Request" or something similar for each of these attempts.
Scenario 2
Consider this URL to create a task under a project: http://localhost:9000/api/project/{projectId}/task
The real-world URL could be: http://localhost:9000/api/project/1/task
or http://localhost:9000/api/project/09abcf21/task
What to do?
Now try to change the projectId as suggested below and hit the API as User A:
Invalid projectId
Blank/Empty projectId
Valid projectId but the projectId belongs to User B.
Worse Case: You get a not-so-user-friendly error for the first two points. The third request creates the task under the project belonging to User B.
Best Case: Your server gives you a "Bad Request" or something similar for each of these attempts.
Scenario 3
Consider resetting the password URL: http://localhost:9000/api/user/password-reset
What to do?
Now try to change the token as suggested below and hit the API as User A:
Invalid token
Blank/Empty token
Valid token but it belongs to User B.
Worse Case: You get a not-so-user-friendly error for the first two points. The third request resets User B's password.
Best Case: Your server gives you a "Bad Request" or something similar for each of these attempts.
There are more
I've only shared a few common ones but this should excite you to find other ways to test the APIs and ensure they are secure. If you are a tester or a developer or a scrum master, give it a go and see how your application works. The you get into the "worse case scenario", it is time to raise some bugs and inform the development team.
Here is my advice if you are an API developer:
Validate the token against the user using it.
Do not take the inputs from the browser as it is. Validate/polish it first.
Always have a Request/Response schema validator.
Do not send any secrets to frontend (API Keys, Passwords)
Make sure the error message does not give out any hints to hackers.
Follow Deny By Default or Principle of Least Privileges.
Implement Role Based Access Control for APIs
Which role can do what
Which role can access what resources
Create the resources as a JSON or YAML and start working towards it
Have smart rate limiters
Traffic only via HTTPS
Perform an API risk assessment.
Do not put secrets in code - API Keys, Hashing keys etc.
Frustrate the attacker:
Add delays
Monitor and block IP addresses
I will have to do another post where I'll also show(code) how to do these with node or java springboot.
Being a developer is such an amazing experience. Ensure you develop secure products and gain the trust of your users.