If you’ve ever geeked out and tried to find out how modern applications are developed, you would have most probably come across the term API. API (application programming interface) is a set of programming instructions that enables software components to communicate with each other. For example, your mobile application probably gets the latest weather data from the weather API hosted on the backend server used by your weather application.
APIs are an integral part of any tech organization. Companies like Uber spend over $590 billion per year integrating different systems, and various tech companies have offered over 24,000 APIs to date. Salesforce attributes more than 50% of its revenue to APIs; the same is 90% for Expedia.com. A massive chunk of a typical tech organization’s expense goes towards the research and development of APIs.
Since APIs deal with sensitive and personally identifiable information on a daily basis, API protection is critical.
Consider a web application of a financial institution that facilitates its users to perform actions such as check their account balance or transfer amounts.
After logging in, when you click on check balance, it contacts the following API:
Here, 123123123 is your account number. It then returns the account balance as the response.
The first attack avenue is that the API only takes the account number as a parameter. Even if you are logged in as user Alice, if you know the account number of Bob, you can provide the ID parameter with Bob’s account number to get his balance.
Similarly, if the API is insecure enough to use the same vulnerable format to tra nsfer the amount, an attacker can leverage that to get free money!
Another vulnerable API:
This is the API for when Alice (account number 456) is transferring $1 to Bob (account number 123).
But what if Alice is an attacker? Alice would replace the to_id with her own account number and from_id to Bob’s account number and exploit the vulnerable API logic to get the amount from Bob’s account.
While this is certainly not the case with modern applications and APIs, there are still various data breaches that occur due to API vulnerabilities. A famous example is the Facebook photo API exposure, which affected 6.8 million users, highlighting the criticality of web API security. And this is exactly what we’re going to discuss in this post.
Keep OWASP Top 10 API Vulnerabilities out
The Open Web Application Security Project (OWASP) is a nonprofit foundation that works to improve software security through community-led open source software projects. One of these projects is the OWASP Top 10 project, where they compile a list of the most common vulnerabilities in a domain.
An essential component of testing APIs for security bugs is to check if any of the OWASP Top 10 happen to be in your API. This is one of the more important parts of securing your SaaS API architecture: making sure obvious vulnerabilities are plugged, especially for enterprise-level APIs.
Broken Object Level Authorization
In this vulnerability, an attacker obtains access to an object by breaching the security of other objects or data streams.
For example, an attacker can perform a GET request on accounts/id/financial_info on an API. Even if the attacker isn’t allowed to request a different id (id2), a breach is still possible. The attacker can successfully get the results for id2 by changing the value of id to id2 in the previous API call.
This can happen if the API backend is verifying the token but is not actively checking if the token is associated with the object that is being requested. A real-life example of broken object level authentication would be opening your neighbor’s door with your house key.
In most cases, the reason behind broken access control is improper permission assignments.
One can prevent broken access control by performing various authorization and verification checks and using the id assigned from the session instead of asking the user for an id.
Broken User Authentication
An inferior practice to prevent unauthorized access is to authenticate users by getting credentials from an API request in plain text. This enables an attacker to sniff credentials from the network and brute force credentials with API requests.
It is not uncommon for API developers to use default credentials on APIs to authenticate API users, which leads to broken authentications and security compromises.
One way to secure against broken authentications is to use random access tokens and short-lived tokens while rate-limiting API requests. The enterprise should employ multi-factor authentication to ensure security even when credentials have been compromised.
Excessive Data Exposure
In various cases, APIs return more data than needed by the application. One example would be a typical password reset functionality.
When a user requests a password reset for the user account “admin”, the client-side application requests the API for a one-time password. That OTP is sent to the account’s associated mobile number or email id. Additionally, the API also sends back the mobile number, and the client-side application will display a few digits to inform the client to which number the OTP has been sent.
Even if the client-side application filters the response, a motivated attacker can extract the complete mobile number/email address from the raw response leading to data exposure. It is the same case with error messages. An application bug can cause it to send error messages in response, revealing helpful information needed by attackers.
To prevent these bugs, one should never rely on client applications for data filtering and always ensure use cases before sending any personally identifiable information (PII) in response.
Lack of Resources & Rate Limiting
Many APIs do not check on requests made by a specific user. In this vulnerability, attackers can send multiple requests until the backend is out of resources and cannot serve any other user. These attacks are more commonly called Distributed Denial-of-Service (DDoS) attacks.
These attacks are effectively malicious attempts to overload a server and its surrounding infrastructure. By causing this disruption of service, this attack can disrupt your consumer’s experiences. Think of it like completely grid locking a lane of traffic on the highway so that no new cars can enter.
Developers should consistently rate limit requests performed by users and make sure they are not causing unnecessary processing.
Broken Function Level Authorization
In this vulnerability, a user can carry out tasks that are not permitted. Consider the following API call:
Here, delete user action should be done only by the administrator. But by discovering this endpoint, an attacker can send requests to delete all users without having the necessary permissions.
The backend should ensure the calling entity has proper authorization before performing any operation to prevent this vulnerability.
In cases where an API is used to modify some values of an object, input is directly processed to modify values without any checks. For example, the following call is made by a user to change the account name:
But malicious actors can specify additional parameters to compromise the integrity of the object. A malicious request would look like this:
To ensure this does not happen, developers should set read-only flags to the properties that are not supposed to be changed by the input. There should also be a proper rate limiter to eliminate the possibility of an attacker guessing parameters with brute force.
If applications you use in your backend are vulnerable and not kept up to date with security patches, attackers could exploit vulnerabilities present in the existing services to compromise the whole application.
To prevent this, one should keep all backend infrastructures and services used in the application up to date with all patches and conduct regular security audits.
Injection attacks are most common in web application breaches. According to the OWASP Top 10 project, injection attacks sit at the top as the most common vulnerability.
Injection attacks occur if the input from the user is wholly trusted and directly processed by the backend. They can result in several attacks such as SQL injection, XSS injection, XML injection, XPath injection, etc. They can also result in an attacker gaining remote command execution on your infrastructure.
The best way to prevent injection attacks is to sanitize input properly before passing it to the backend.
Improper Assets Management
An example of improper assets management is leaving legacy code on the application even if there’s no use for it. This oversight can result in a possible entry point for attackers if the legacy code is not maintained correctly. It can also compromise a newer, patched, and maintained application that uses the same infrastructure as the old one.
Regular audits must be conducted to manage assets properly.
Insufficient Logging & Monitoring
Many large-scale deployments set up infrastructures for logging and monitoring to keep an eye on events related to the application. But due to poor designing, logs are not properly monitored and integrated with security information and event management (SIEM) systems.
Securing Your API
Having explored and elucidated upon the OWASP Top 10 API Vulnerabilities, the next step in this checklist is to finally internalize how exactly you can secure your API from having these vulnerabilities exploited. Think of it like API governance: you’re deploying a set of standards and checks for your SaaS API management. These are methods and tactics you can use to protect your application API from attackers, as well as plug any vulnerabilities that may have been created in the initial build phase.
One of the best practices is to use Json Web Tokens (JWT) or OAuth to authenticate your API.
Authentication through JWT relies on various details provided as a token with the API request.
Data contains a 64 encoded header, payload, and signature, all in the JSON format.
Java Web token is provided with the request, so verification becomes easier. For verification, the header and payload can be re-encrypted with a previously shared secret key to ensure the signature is genuine. If it is genuine, it can be deduced that the data can be trusted. The authorization portion of the payload in JWT can be used to identify the user without checking every time.
OAuth allows websites and services to share assets among users. A typical example of OAuth would be when a user is provided with more than one service to authenticate himself. For example, the user chooses to authenticate with an email service website and is redirected to the login page of the email service.
After successful authentication on the website, the user is redirected back to your application but with an additional access token of the email service. That token can be used to verify user identity. OAuth also provides the functionality to implement single sign-on (SSO) to provide a better user experience.
Implementing JWT and OAuth
As JWT is simply base 64 encoded, no sensitive data should be passed with JWT. The time to live—the expiration period for a token—should be as short as possible. Cryptographic algorithms should be hardcoded into the backend, providing no option for the client to change the algorithm. The JWT Secret should be random and complicated to make it infeasible for an attacker to brute force a secret key. If using OAuth, redirect-uri should be validated on the server side to make sure only whitelisted URLs are allowed. Also, while using OAuth, scope should be strictly defined and later verified for each request.
To prevent DDoS and brute force attacks on your API, it is essential to implement a rate limiter to make sure only legit requests are satisfied. Rate limiters are softwares that constraint the incoming request to a web server. So, if an application is getting more requests than configured from an IP, the rate limiter will drop packets from that IP for a duration defined by timeout.
For example, in NodeJS, the Bottleneck package can be used to limit the number of concurrent connections as shown in the following code snippet:
Same way, ratelimit can be used to implement throttling in Python-based APIs.
As discussed before, input sanitization is an essential requirement for any application dealing with user input. Even a single special character like an apostrophe (‘) can result in a security compromise.
Consider a login page that uses SQL database as its backend. The webpage first takes the input from the login page and transforms that input into an SQL query. For example, with username = bob and password = alice, the SQL would be generated as:
However, as there is no input sanitization in place, attackers can log in to the website with any account without a valid password.
To perform the attack, they would pass bob’– as username instead of bob and any random string as password. So, the new SQL would be:https://carbon.now.sh/?bg=rgba%28171%2C+184%2C+195%2C+1%29&t=seti&wt=none&l=sql&ds=true&dsyoff=20px&dsblur=68px&wc=true&wa=true&pv=56px&ph=56px&ln=false&fl=1&fm=Hack&fs=14px&lh=133%25&si=false&es=2x&wm=false&code=SELECT%2520*%2520FROM%2520USERS%2520WHERE%2520user%2520%253D%2520%27bob%27%27–%2520and%2520password%2520%253D%2520%27hackallthethings%27
As the ‘– in the username makes everything after the user field a comment, password checks become disabled, and the backend will allow the attacker to log in without a valid password.
Thankfully, NPM has a module, express-validator, which can be used to sanitize input in forms before actually processing it. Following is the example usage for the express-validator library.
With express-validator, you can use a simple isEmail() function to verify if the input is an email address or not.
Similarly, you can use check(‘value’).matches(‘regex’) to test if the input value matches the required input format specified by the regex.
Being an attractive target for attackers, APIs are predicted to be the top attack vector soon.
The only way to protect your API from attackers is to take proactive measures to secure your API design.
The best way to do this is by following an API Security checklist, such as structured in this article. Make sure you’re familiar with the top vulnerabilities of the time through OWASP or other such resources. Then, understand the methods you can use to secure your API from common attacks and learn how to implement them successfully. There’s a lot more to learn in API security, but this is a good starting point.