As cybersecurity attacks become more and more common, it’s extremely important to secure your APIs. However, some developers neglect securing their APIs if they believe their APIs are only communicating with the frontend of their programs. There is this misleading perception that a well-secured front end excuses you from paying too much attention to related API security. The truth is that you need to pay equal attention to securing your APIs regardless of whether they communicate with the frontend or the backend of your application. In this post, you’ll learn the top 7 security best practices for APIs.
Pay attention to user authentication/authorization
Incorrectly setting up the user authentication process leads to dangerous situations in which an attacker can take over user accounts without cracking the passwords. Some developers believe that merely installing an authentication library keeps user accounts safe. Well, that’s not always the case. Sometimes you need to tweak the config of that library for it to be effective. And sometimes the library leaves some endpoints of your API vulnerable. You should always double- or triple-check that your user authentication mechanism doesn’t have flaws. Or, when choosing which library to use, go for proven solutions that implement OAuth2.0.
Implement access control
Modern applications are usually built as microservices. This means that you’ll most likely have more than one frontend application connecting to your API. On top of that, you’ll probably have some other microservices that will need to read data from your API or write data to it. In the microservices world, it’s worthwhile to implement access control to your API following the least privileged principle. The microservices that talk to your API should only be able to access the endpoints they need, not the whole API.
For example, if you have some microservices that only need to consume the API, then they should have read-only access. And if another microservice needs to write and read only to and from the /orders endpoint, then it should only have access to that endpoint. If you want to learn more about securing microservices specifically, you should check out this post.
Protect yourself from injection attacks
Injection attacks are one of hackers’ favorite techniques. There are a few types of injection attacks, but when it comes to API security, you mainly need to avoid SQL and OS command injections.
The first, as the name suggests, allows the attacker to inject malicious SQL code into your application. Since the API usually acts as a gate to the database, injecting SQL code can give the attacker the ability to wipe your database or get access to all your sensitive data, including user passwords. Luckily, it’s easy to avoid about 90% of SQL injection attack attempts. All you have to do is use parameterized statements instead of directly passing the user input to the SQL query. Another option is to escape all user input before using it in your SQL query. You can also try to avoid writing raw SQL queries yourself and use an ORM framework instead. These frameworks abstract the database operations and prevent SQL injections under the hood, although they are not perfect.
OS command injection
Let’s talk about OS command injection. Sometimes your API will have to execute a command on the underlying operating system. There can be many reasons for this. For example, you may be using a legacy application that doesn’t expose its own API and can only be used as a command tool. If you then use user input as a parameter for that command tool, you’re pretty much exposing yourself to an OS command injection attack. An attacker can try to execute literally any other command on your system by manipulating the input data.
The best solution is to never call OS commands from your API. If this isn’t possible, then you need to validate the user’s input before sending it to the OS command. Depending on the input you’re expecting, you can also limit the accepted values. For example, if you only need a number from the user, then make sure that your API won’t accept anything that’s not a number.
Don’t send too much data to the UI
As mentioned at the beginning of this post, a secure frontend doesn’t mean you don’t need to pay attention to securing your APIs or the backend. Exposing too much data to the frontend perfectly illustrates the necessity of backend security. Imagine a situation where you need to pass some user data to the frontend, such as a user’s first name and last name. Unfortunately, a common practice in these cases is sending the whole user object to the frontend and filtering for the required fields there. This practice may be easier and quicker than filtering information first, but it exposes all the user’s data to anyone who bothers to look for it. All an attacker needs to do to compromise an account is open the developer console in the browser. From there, they’ll be able to see all the data you tried to filter out. The solution here, as you may guess, is to never send more data to the frontend than necessary.
Set up rate limiting
Another issue that comes from the same “a secure front end is enough” way of thinking is the lack of (D)DoS protection. You may be thinking that you won’t get enough requests from the front end to overload your API. But a smart attacker won’t need to touch your front end at all. They may just find the DNS name or IP address to your API and perform a (D)DoS attack on it directly. This can cause your API to crash. And if the attacker doesn’t stop, your application may be down for quite a long time.
The same holds true for brute force attacks. Attackers can target your APIs with brute force to try and exploit them. The solution suggests itself: don’t treat your front end as a shield for your API. Implement rate limiting on your API to protect against (D)DoS and brute force attacks.
Implement secure headers and CORS
There are a few additional HTTP headers that your API should support to deter attackers. Depending on the language and libraries used, you may need to add them yourself. Otherwise, you’ll be able to enable them with one parameter. You can find the list of HTTP headers here.
Another must-have mechanism is cross-origin resource sharing, or CORS. CORS defines who’s allowed to talk to your API. If your API serves only the frontend application, then you should definitely use CORS and only allow that frontend application to access CORS and talk to your API. This simple yet powerful mechanism can prevent a few other attack vectors.
Log the right information
You may be thinking, what does logging have to do with security? For one thing, it’s possible for you to accidentally log user passwords in plain text. An attacker would have a field day with that information. When it comes specifically to API security, you can have either insufficient logging or quite the opposite. Both situations compromise your security. Insufficient logging means that attackers can safely try to exploit your API over a long period of time without being detected. If you don’t log enough events from your API, how will you know if someone is trying to hack you?
On the other hand, logging too much isn’t good either. That creates a problem where if your application crashes, the API will print too much information for the user in the crash report. This information can include the exact path to the file which caused the issue, the names of the libraries used, or the database schema. Including too much information in the logs can make things a lot easier for an attacker.
Summary: Your frontend and API security work together
As APIs continue to proliferate, it’s important to secure them across the board. This includes securing APIs that only communicate with the frontend, which should never be treated as a secondary task. Don’t treat the frontend as the API’s shield.
In this post, you learned the importance of securing APIs. To find out more about possible API related attacks, check out the OWASP API Security Project. If you want to go even further, you can look into encrypting traffic using TLS between your API and its consumers or implementing an application security solution with your APIs. But as a starter, make sure that you follow these seven best practices.
This post was written by Dawid Ziolkowski. Dawid has 10 years of experience as a Network/System Engineer at the beginning, DevOps in between, Cloud Native Engineer recently. He’s worked for an IT outsourcing company, a research institute, telco, a hosting company, and a consultancy company, so he’s gathered a lot of knowledge from different perspectives. Nowadays he’s helping companies move to cloud and/or redesign their infrastructure for a more Cloud Native approach.