How I've Abused an Internal API and How It Should Be Prevented

It was suprisingly easy, but there were great lessons after that.

ยท

5 min read

As a podcaster, I usually schedule a quiet learning room slot in the public library, for recording. The rooms are free of charge, and therefore they are fully booked just when the scheduling platform is open, 9 days in advance.

I was investigating a way to book these rooms earlier. First, I planned a scraper cronjob, but then I found a better way for that, by abusing their internal API.

So let's demonstrate some critical steps I've taken, and how all this fun should be avoided on the server side.

Getting The API Address

First, since the library's website runs on top of Wordpress, I was pretty sure that the scheduling page is programmed and is not a part of the WP platform. Therefore, I quickly found the js file that handles the booking page.

There I found that the client makes an ajax call to a URL saved in a variable the_ajax_script.ajaxurl:

jQuery.ajax({
            type: 'POST',
            url: the_ajax_script.ajaxurl,
            dataType: "JSON",
            data: bookRoomData,
            ...
        })

I got its value from the console, double-checking with the post requests from this website:

got the value of the variable from the console

Can I Make Calls To The API From Another Client?

Now that I had the internal API address, I wondered if I could make some calls from my own client. Using Postman I succeeded in scheduling a room, and abusing the internal API even more than 9 days in advance:

Successful API cal by Postman

Building My Own DB & Client

I played again with the original client to understand the key-value mechanism of the specific room, slot and day, i.e. how it saves the slot (date, hour, room) in its DB. The administrators made it really easy for me because they save the 'body' for the POST request in a bookRoomData variable, and update it on every change in the form. So I checked some different values, queried the console with this variable, and got all the keys I need.

Now building my own client was pretty straightforward. I had the API address, I could build my own DB of dates, rooms, and slots, and I knew that they don't block the 9-days-in-advance on the backend side.

The only problem was the CORS policy, which forced me to build a small server, just for making the POST call to the library server. So I called with POST req to my server and called again from my server to their server.

How Should Abuse Be Prevented?

Now that we've had some fun, let's approach the situation more seriously. Abusing the scheduling application was easy, way too easy! There wasn't even a single constraint!

So let's talk about basic steps to avoid these attacks:

Server Side Validation

The validation of booking fewer than 9 days in advance happens only in the front end. The goal of front-end validation is nothing more than User Experience. For example, instead of getting an error from the server when a new password doesn't fit the requirements (e.g. one special character, digits...), informing the user in the front end will increase the experience, and will help the user to immediately enter another password.

The keeper of the data is the server, and therefore the backend is in charge of final validation before fulfilling the request. If they had paid attention to that, I wouldn't have succeeded in booking a room more than 9 days in advance.

There are several ways to 'full-stack' validation alignment, with shared data models between the client and the server. For example, Remult does it as a part of their CRUD framework for fullstack TypeScript.

Make the API Internal

Another way to make things a little bit harder is by paying attention to the referer in the request header when the API is internal. If the referer isn't the certified client, block the request.

However, faking the referer value is a no-brainer for a determined user.

User Authorization

User authorization, and blocking any unauthorized calls, could prevent a lot of malicious and unwanted calls to the API.

In our case, maybe just for learning room slots it's an overkill to manage user registration, but if you would like to secure the API, this is basic.

And of course, even with user authentication, there always remains a hacking risk.

CSRF Token / Same-Site Cookies

CSRF (Cross-Site Request Forgery) is a type of web security vulnerability that allows an attacker to send malicious requests from a victim's browser. This can allow an attacker to perform actions as the victim on a web application in which they are currently authenticated. CSRF protection can help us to make sure that the actions of the authenticated user are from a trustworthy client.

To implement CSRF protection on a web application, you can follow these steps:

  1. Add a CSRF token to the application. This is a unique and random value that is generated for each user session. It is typically stored in the user's session data, and added to the form as a hidden field or to the headers of requests made via JavaScript.

  2. Verify the CSRF token on the server. When the server receives a request, it should check for the presence of the CSRF token and verify that it is valid. If the token is not present or is invalid, the request should be rejected.

  3. Add a same-site cookie attribute. This attribute tells the browser to only send the cookie with requests made from the same domain. This can help to prevent the CSRF token from being sent to a malicious site.

  4. Use a unique and unpredictable token. The CSRF token should be unique and unpredictable, to make it more difficult for an attacker to guess or forge a valid token.

  5. Expire the token. The CSRF token should have a limited lifetime, after which it is no longer valid. This helps to prevent an attacker from using a stolen or leaked token at a later time.

  6. Use HTTPS. Using HTTPS can help to prevent an attacker from intercepting the CSRF token, as it encrypts the communication between the browser and the server.

ย