REST API is commonly used for the communication of microservices and front-end and back-end parts of WEB and Mobile applications. It has become popular because it is intuitive, highly scalable, and easy to modify and extend. This layer hides the info from the user that he shouldn't see. So this layer can be used as the target for cyber-attack. According to Salt Security, 91% faced security incidents in production last year. In this article, I would give you some pieces of advice to help you build a secure API service.
HTTP is an awesome application layer protocol, but it is not secure for login data or credit card information because it doesn’t have encryption. Cybercriminals can intercept requests with users’ sensitive data by breaking into the Wi-Fi connection or the internet provider’s network. This type of attack is called Man-in-the-middle(MitM) attack and it cannot be carried out if the site uses HTTPS.
The main idea behind HTTPS is the generation of a short-term key for a single session and verifying it with a long-term client-side public key and server-side private key. A session key is generated by the owner of the public key and is encrypted by it. This encrypted key is sent to the server and only the server can decrypt it. Now only the client and server know the session key with which all requests and responses are encrypted. Such type of encryption is called Hybrid cryptosystem and SSL/TLS are good examples of such layers.
WebSockets can also work over SSL/TLS encryption which also perfectly protects against the MitM attack.
To implement an encryption layer over your HTTP you need to gain an SSL certificate from a certificate authority. You can get it even for free with services like Let’s Encrypt. Let’s add an SSL certificate to a sample page running in Nginx.
As you can see Google Chrome shows that my site is not secure. To add an SSL certificate you need to enter your system info on the Certbot website.
Now, even when cybercriminals intercept your request, it will be impossible for them to retrieve any information. It is the first and the easiest step in the way of creating a secure API.
But it also has a few disadvantages. Encrypted connection involves more computations to encrypt and decrypt data. As a result, it affects the performance of both the server and the client and introduces larger latencies. Another issue that can force you to not implement SSL/TLS encryption is the impossibility to use caching in proxy server between server and client.
Cross-origin resource sharing is a browser mechanism that controls access to resources. This extends the Same-origin policy which restricts scripts on one origin from accessing the data in another and is used by default in all browsers. This prevents requests from third-party sites to your server.
On the server side, you can restrict domains of sites that are allowed to send requests. Browsers will block requests from other domains. Also, it is possible to restrict the methods and headers of specific requests. For example, if I want to accept only GET requests from https://foo.bar I can add such headers to the response
For applications that don't use cross-domain requests, CORS should be disabled so the browser will restrict any requests. But if we are setting these headers in the response, how does the browser know about them before sending a request? It sends an extra handshake using the HTTP OPTIONS method to determine if the actual request is cross-origin compatible.
But you don’t need to do it manually, nowadays, all frameworks have built-in systems to implement CORS.
Object Level Authorization
This is the mechanism that checks if a user is allowed to do any manipulations against the data(create/read/update/delete). According to OWASP, it is the most common pitfall of modern APIs.
Here is an example of the problem:
We have authentication and it feels secure. For updating some product information manager uses such a request.
The customer registered on the website somehow found this request and tried it to make a joke (or not). He has just authenticated and sent the same request. The product was updated. He tried other ids 2,3,4 etc. and the request worked as well. He updated the product, moreover, he can update any product. But he is not the manager and not allowed to do it
Of course, you don't want to allow someone to be able to change some data. For this purpose, you need to implement access control security. For example Role-Based Access Control. It allows restricting control based on user roles and privileges.
In our server-side application we can define two roles: manager and customer with permission to read for both, and to update/add/delete only for a manager. We can assign these roles to users and save them in a database with user information and insert them in the JWT token. During the authorization process, we determine the user's role and allows to make changes in the product only if the user has permission to do it.
Also, it is possible to build a hierarchy with roles, so users with higher-level roles have all permissions of their sub-roles. This makes the process of maintenance user permissions easier.There are more other access mechanisms here
The issue with excessive data exposure is very common. Some developers rely on the front-end part to show not full data, and they throw all info from the database in response. It is the wrong approach. In the database, much private information can be saved like address, credit card numbers, etc. The backend part of the application should not rely on the front-end, it should work independently and return only that information that the user needs.
Let's imagine some social network, with a search system of users where they can find each other by name. The record with such structure is saved in the database:
The search system returns the list of users, and only the name is shown for the end-user. But here is the problem, even in dev-tools in browser users can see the FULL response with phone number and email.
Such problems even have big companies with a lot of resources, like Google. In 2018 Google got into a scandal with a data breach. After software update Google+ API. Large number of apps that used it got access to data that users marked as private. User data from over 52 million users were exposed.
Also, it is not only a security problem, when you transmit full data, it is always bigger than the user really needs so it causes additional network load.
The other great solution to fix this problem is to use GraphQL. It is a query language for your API that provides the possibility for a client to ask only for data that is needed. But don’t forget to check user permissions in your resolvers or middleware to block access to not allowed data.
Web Application Firewall
Web application firewall(WAF) is a network security system for applications that use HTTP/HTTPS protocols that filter and block network traffic based on some security rules. This system helps prevent large amounts of attacks like DOS/DDOS, SQL injections, Cross-site request forgery, etc. Also, it provides functionality for blocking and tracking IP addresses, adding HTTP security headers, and others depending on specific realization. This tool also can analyze normal user behavior, which helps to detect attacks using automation tools like brute force and DDOS. There are a lot of free and paid systems on the market.
Here is how the Azure WAF is working. After creating you can attach it to the Application Gateway and it will restrict all attacks that it knows. Azure WAF has a lot of predefined rules to secure you from the most popular attacks.
To show the work of WAF I’ve created a Virtual machine with the sample apache default page and Application Gateway for it.
A simple XSS attack through the public IP of a VM is successfully (for the attacker) executed.
But the same attack through our Application Gateway with WAF is blocked with status 403.
It blocks in the same way a lot of others, more destructible attacks. It is also possible to define your own rules to restrict attacks you are experiencing with your application.
DOS and DDOS protection
Denial of service attack called up to overload the system making a bunch of slow requests, so the general user is unable to access the system because it is working at its limits. The standard way to prevent it is the implementation of lockout (prevents making a certain number of requests from a single IP) or progressive delay (adds delay longer than previous after each bad request). The good practice is to set a max timeout per each request so it. There are a lot of libraries for all popular frameworks. Or the better solution is to run load-balancer like Nginx to Nginx advices
Distributed denial of service attack is one that is hard to overcome without side-party solutions. The traffic comes from a bunch of sources that makes it impossible to stop it by simply blocking sources.
A good idea to partly prevent it is to use a content delivery network, where servers are located in different places and combined in a single network. So the user will access the nearest server.
Another simple idea is to provide a captcha for anonymous requests.
Use Azure/AWS gateways or virtual networks with built-in DDOS protection
It is awesome to implement all these recommendations in your project. But during the development process, many things can be changed, after fixing issues, already working logic may break. So it is very important to have security tests. You should create integration tests that should cover not only positive cases but also check if users don’t have permission to do some data manipulations. Very important to cover all requests.
Also, there are more technologies to help find security pitfalls, the two most popular are SAST and DAST.
Static Application Security Testing (SAST): the main idea is to check source code and find security vulnerabilities. Tools that use this technique find the patterns or rules in source code that can lead to flaws. This approach can be used in any stage of the development process; some tools analyze code even in IDE.
Dynamic application security testing (DAST): here we know nothing about source code, we just try to execute attacks and check what is going wrong. DAST tools allow detecting vulnerabilities with minimal user interactions to detect a lot of runtime and configuration security vulnerabilities.
Don’t forget about stress-tests attacks. There are a bunch of open-source tools to check the performance of your application and how many users it can process. One of such tools is JMeter. It has a simple GUI where you can configure multiple scenarios for different routes from multiple threads.
It shows all responses during the test that will be useful for debugging. Also, it is possible to generate a report with statistics etc.
Your API service is the communication center in your application. It should be like a castle on a hill inaccessible on all sides. Of course, implementation of new features is very important but losing the private data of your users or any sensitive info of the company is the last thing you want to deal with. So it is important to allocate time for writing integration tests and configuring SAST and DAST tools.