September 17th, 2023
In the age of data breaches & everything digital, there was never a lesser need to build secure APIs. Insecure APIs means:
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.
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:
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:
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.
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.
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:
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.
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:
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.
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:
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.
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.
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.