REST API – Prevent Abuse to REST API Endpoint

apiauthorizationrestSecurity

Question 1

I have a login API endpoint

http://127.0.0.1:8080/api/account/login

It checks if the phone number has been validated. If not it will call

POST http://127.0.0.1:8080/api/account/sendsms  country_code=1, phone_number=1234567

How can I prevent abuse to requesting sms? (It'll cost money on my part. I also have throttle function)
I only want sms to be sent if /login failed (at this point email has been validated)
The way I do is to send a custom header X-TOKEN-SMS which acts like an authorization header with jwt token. X-TOKEN-SMS jwt token is created at /login if phone number is not validated = True.
When /sendsms decodes the jwt without error, proceed with requesting sms.
If jwt decodes with error then the front-end will redirect to /login.

Is this method a good practice?
How do you allow an API endpoint to be accessed only if some endpoint has been accessed?

Question 2

Let's say I have an endpoint that can only be accessed by an authorized user

http://127.0.0.1:8080/api/account/points

The user can GET points(retrieve all of his/her points)
POST points (create a new point) or PATCH (update a point)

How can I prevent an authorized user from abusing the POST /points? They might add points as they please, which is not what my app want it to be. Adding points should be handled by the app logic.

If adding point should not be consumed by REST API, then create a new row directly to the db is more secure?

I found a similar question asked before Secure Rest api from authenticated user
But couldn't seem to understand it.

Best Answer

For the first question, your approach seems correct to me. You consume a token in the sendsms entry that can only be generated by the login endpoint. You could also use a generic token created by login, and not easily guessable (it would be equivalent to a cryptographically generated session ID), used as primary key to a database tuple. There you can store whatever you want (for example the number of SMS still allowed to be sent).

You can also store the "state" in a variable and protect it using, say, HMAC, continuously exchanging it between the server and the client, but you still require some server state to defend against replay attacks on non-idempotent endpoints; at minimum, you need a per-session incremental counter.

The second problem is trickier, and you need first to ask yourself what the difference in behaviour (as seen by the server) would be between the app with its logic, and a malicious user sending repeated requests. You might enforce a minimum delay between requests that is comparable to the maximum rate at which the app can be driven, so that there's no longer an advantage in using tricks rather than going through the app.

Related Topic