How To Spot Malicious API Behavior

Malicious actors are often hard to deal with. It’s one thing if an attacker is proclaiming their intentions and making it clear what they’re trying to do, but it’s something else entirely when the lines between abuse and uninformed use are blurred. Is this a user who doesn’t know the API is rate limited? Is it a malicious actor or bot trying to break the system entirely?

Below, we’ll explore some best practices you can apply to prevent the common methods malicious actors employ to undermine web APIs. We’ll consider what makes a call malicious and see how we can delineate these actions from typical use.

What is a Malicious API Call?

While certain behaviors are clearly hostile and illegitimate, others may fall into a gray area. For example, high-volume requests without the relevant permissions are a red flag. Yet, it may simply be a user who misunderstands the system or applies the wrong key to their calls. There is simply no one-size-fits-all definition that results in 100% coverage of all malicious behavior. Your best bet, then, is to create a sliding scale of behaviors between “reasonable” and “potentially malicious.”

What, then, should be considered reasonable behavior? Reasonable behavior is best defined as the actions that a regular user would take using average amounts of resources to accomplish a typical goal. This may seem ambiguous, but defining “reasonable” actions is largely determined by the API in question. Let’s take a look at a use case.

Let’s assume that we have an API that hosts data about compact discs for a local audio store. Appropriate behavior for a user of the online API may be to look up a set of albums from a specific artist or genre, retrieving their costs and, potentially, setting a request for reservation of that album. This is a good baseline for a reasonable use case. On the other hand, it would be highly atypical for a user to request the entirety of the store stock, reserve 200 albums, or submit the same request 300 times. This behavior is not necessarily malicious, but it does begin to slide down the scale towards something that could be considered so.

Let’s look at a use case that would fall somewhere in the middle. The same user is now requesting a high volume of albums and then adding a reservation request. The user has submitted 90 API calls for 90 albums. While this may indeed be malicious, we can look at the type of request. Suppose we see that the user is searching for “grunge” or “pop” albums and that the reservations are made instantly. In that case, it is more likely that the user is well-intentioned but inexperienced, and is using CLI functions without much knowledge of their application. This is a gray area, and if it only happened once, we could make a strong argument for it being incidental to regular use instead of malicious.

However, if we go one step further, we can clearly see malicious behavior. The user is making the same 90 API calls, but upon adding a reservation for the album, is attempting to upload a large number of text annotations to the item in question. Because the API filters out large text chunks, the store is not in any immediate danger. But, given how large the text is and how it’s uploaded, this would point to an attempt at breaking the system. Such action is most likely a deliberate attack on the system.

Accordingly, we can see that these issues are not black and white — API developers will need to understand common malicious behavior categories in order to detect these issues.

Why Aren’t Firewalls Enough?

All of this conversation may beg the question — why aren’t firewalls enough? Firewalls are a complicated issue when it comes to security, especially as the line between “dumb” and “smart” routing and protection has changed drastically in recent decades. Generally speaking, firewalls are an excellent tool to get you started with security, but they fail due to a simple fact. The difference between malicious and reasonable often hinges on long-term monitoring and tracking, and the gray area between the two is often unclear.

Firewalls, broadly speaking, work best against known threats. A firewall is very useful if you know the attacker’s IP, the type of attack being sent, etc. Even if you don’t know all of that information, some basic heuristics systems can detect certain aspects of an API call that makes it possibly malicious and can act upon that call. The problem is, of course, that attacks seldom come with a warning label and seldom with advanced warning. Very few attacks will instantly raise alarms. Most malicious behavior will begin as close to typical user behavior as possible before escalating and acting upon detected points of failure.

For this reason, firewalls create a dichotomy that is often difficult to parse — the balance between a normal user who remains normal and a normal user who becomes malicious. At what point do you ban traffic from an IP or a user? Is it after the first “attack”? That attack could very well be a mistake, solvable through education rather than punitive measures. Firewalls are part of a system of security. They’re a bit like the front door of a house in that they will keep out most bad elements, but without a good roof and strong windows, you are just as open for disaster.

Malicious Behavior Best Practices

While there is no one-size-fits-all definition, providers can adopt certain best practices to detect malicious behavior. This list is non-exhaustive, but it does represent a large amount of common malicious behavior types, and as such, forms a solid introductory approach towards dealing with this traffic.

Watch for High Rate Requests

High rate requests are always signs of malicious behavior, but they certainly aren’t typical for most users. If the user is simply uneducated, the requests can still create API congestion and processing load that is unreasonable and unnecessary, impacting the user experience negatively and resulting in poor API quality. Beyond basic denial of service, there are concerns of buffer overrun and code injection, which can turn this type of behavior into an active assault.

The best way to deal with these sorts of issues is to make them impossible in the first place. Lock high volume and high rate requests behind premium features or trusted roles. While a paywall won’t deter every malicious behavior, it will limit most opportunity attacks on low-hanging fruit.

Others will be more committed to the attack. And, the more barriers you place, the more likely you will get rid of fly-by attacks and opportunity-based attackers. Here are some additional ways you can stop these attacks in their tracks while still allowing you to monitor, measure, and respond:

  • Proper implementation of code stripping (preventing code execution and injection).
  • Automatic truncating of text beyond a certain limit.
  • Time-based rate-limiting.
  • Pushing excess requests to a null endpoint.

Watch for Attempted Privilege Escalation

Privilege escalation is a hazardous type of attack, and more often than not, the mechanisms that enable it are not entirely apparent to the average user. Essentially, this type of attack locates a mechanism internally that allows users to elevate their own privileges. By leveraging this mechanism, a user can enter the system, pretend to be a standard user, and then slowly and laterally move into positions of higher power to execute malicious code and enact a nefarious plan.

Preventing privilege escalation is not always glaringly obvious, and in many cases, it may leverage functionality that was designed with the best intentions. A good example of this kind of escalation can be found in this article by Ronak Patel. The API in question contained an endpoint that allowed for privilege escalation in a way that the developers never intended, utilizing a function core to reporting for lateral escalation. Through these kinds of minor gaps in the code, malicious actors could find ways to elevate their permissions, thereby bypassing much of the security apparatus protecting the API.

The easiest way to watch for this behavior is to track behavior outside of expected parameters. In the above example, proper flagging for a non-developer account suddenly having developer permissions should have failed the request. Or, at the very least, it should have made the developers aware that the request was made. If you can track this behavior and respond quickly, you can mitigate damage. Moreover, actively check for privilege escalation gaps, as this vulnerability should not be possible in a well-designed system.

Create Heuristics and Location Data

Location-based heuristics can be a strong indicator of malicious traffic. The basic idea of a heuristic is to take a measurement at the baseline and then track deviations from that baseline. While general heuristics can be applied for most of these solutions, location-specific heuristics effectively prevent a specific kind of malicious traffic — account takeover.

An account takeover is when a user account is utilized to perform functions that the actual owner did not request. This can come in the form of exposed user credentials, such as when a user utilizes a shared password. But, it can also come in the form of accidentally exposed credentials, such as a GitHub commit that includes credentials for internal systems.

An easy way to mitigate account takeover is to track the heuristics of a user’s location data. Do all of your developers work out of Poznań? Are your company laptops Mac by requirement? If a Windows machine uses your credential in Barcelona, a heuristics system should halt the request, and request additional information before committing to the API call. In other articles, we’ve explored how a hypermedia-based workflow is an effective way to trigger these authentication workflows on the fly.

Track Mapping and Scanning Attempts

An attempted mapping is an early sign of malicious API behavior. While there are many reasons a developer may want to map an API, it should be considered a red flag. The threat level should thus scale with how open the API is — if you provide an open specification and the endpoints are clearly documented, scanning the API may not be a threat. It may instead be someone who has not read the documentation.

On the other hand, if you have undocumented endpoints, or endpoints that are internal and only reached through external routing, then mapping and scanning efforts can point to someone trying to find the weak point in a defensive matrix.

The best way to handle mapping is to understand your API’s expected behavior and prevent deviations. If it’s normal for your API to have its endpoints known and to have verbose instruction sets between them, then someone scanning the system is probably no cause for worry. If this is not the case with your API, steps must be taken. Proper throttling, credentialing systems ensuring authentication and authorization, and behavioral tracking can help ensure this activity is kept to a minimum.

Trust But Verify

Simply put, malicious behavior is not always clearly delineated from normal behavior, and as such, developers need to be on the guard when it comes to any API activity.

One perspective is to adopt the mantra of “trust but verify.” Assume goodwill, but also assume that if the intent is good, the user won’t mind an extra step to carry out that function. Simple heuristics, credentialing, and rate-limiting will likely not impact the average user, and as such, are easily deployed against malicious end users, who would be impacted.

Nowadays, it’s also essential to adopt a zero-tolerance policy to treat internal-looking requests with the same rigor as external callers.

What do you think about these solutions? Let us know in the comments below.