Testing OWASP’s Top 10 API Security Vulnerabilities Posted in Security David Balaban November 11, 2020 Compared to web applications, API security testing has its own specific needs. Below, we cover the top vulnerabilities inherent in today’s APIs, as documented in the 10 OWASP API security vulnerability list. We’ll provide ways to test and mitigate each vulnerability and look at some basic tools to automate API security testing. APIs Are Vulnerable to Attack OWASP maintains a list of the top ten API security vulnerabilities. First, just how vulnerable are APIs? Consider one API exploit that allowed attackers to steal confidential information belonging to The Nissan Motor Company. In 2016, a vulnerability was discovered in the API of the Nissan mobile app that was sending data to Nissan Leaf cars. A group of researchers found that using API, you could send commands to any vehicle if you knew its VIN number. The Nissan API vulnerability exposed climate control, battery management, and many other car functionalities. Hackers could access the entire history of a car’s trips, including route time, exact GPS route coordinates if the driver broke any traffic rules, and other confidential data points. Private information like this should be very well protected, yet it was exposed through the API. So, what place do APIs occupy in our lives today? According to statistics, on average, each big company uses about 420 services, and 83% of all traffic on the Internet today belongs to API-based services. With the arrival of 5G and especially the Internet of Things, we expect that traffic between API services and apps will only grow. Today we live in a world of microservices, containers, and clouds. The days of monolithic architecture are over, meaning we must change the approach to security that was established many years ago. As you may know, OWASP publishes the top 10 vulnerabilities reports every year for different application types. Recently, OWASP launched its API security project, which lists the top 10 API vulnerabilities. Let’s go through each item on this list. 1. Broken Object Level Authorization The first vulnerability on our list is Broken Object Level Authorization. Let’s say a user generates a document with ID=322. They should only be allowed access to that document. If you specify ID=109 or some other ID, the service should return the 403 (Forbidden) error. In reality, everything is a little different. Often, not all services are checked for access controls. You can often change a resource ID and access a document with content meant for another user. To test this issue, what parameters can you experiment with? You could pass any ID in the URL or as part of Query parameters or Body (in XML or JSON). Try changing them to see what the service returns to you. Unfortunately, there are no automatic tools where you can press one magic button and get a detailed report. So far, Broken Object Level Authorization can only be tested manually. You can use the same tools with which you usually test APIs like Postman, Fiddler, ReadyAPI. So, send different requests and analyze the responses. During the first round, you must define these cases manually. Later you can automate them as part of your normal functional testing. 2. Broken User Authentication The second type of flaw is called Broken User Authentication. I hope you know the difference between authentication and authorization? All vulnerabilities that are related to authentication are usually associated with password management mechanisms and login mechanisms. To avoid Broken User Authentication, passwords should be long (at least, say eight characters), including uppercase and lowercase letters, and so on. You also need to ensure it is impossible to bypass the login procedure and access objects or pages without being authenticated. In this case, the service should return 401 (Unauthorized). You must also ensure that a brute force attack cannot be run, as well as that the Forgot Password functionality does not return the password in clear text to you in an email, and so on. When users log into an application, they are assigned a unique session token. Almost all vulnerabilities associated with sessions are related to this session token. Here, you can test whether this session token gets reassigned after each successful login procedure or after the access level gets escalated in the application. In case your application removes or somehow changes the session token, check to see whether it returns a 401 error. We must not allow the possibility of predicting the session token for each next session. It should be as random as possible. In addition to working with the login procedure and session tokens, it is also important to remember that APIs communicate with the client (UI) and other APIs. In these scenarios, it is common to use the client’s session token for further communication. However, developers do not always follow this practice. They often use special service accounts for API-to-API communication. Thus, the API may have access to more data than intended. Unfortunately, this kind of vulnerability cannot be detected even if using black-box testing. Such vulnerabilities are only found during code review or architecture review. Broken user security issues can also be associated with different approaches to authentication. There is basic authentication and claims-based authentication, and the application can implement Single Sign-on. Each of these mechanisms has its own set of vulnerabilities and best practices. OWASP offers detailed checklists for each of them. Developers must ensure authentication mechanisms are correctly set and secured. Several automated tools may help you test the most common authentication patterns. For example, for basic authentication, security tools like Acunetix or Burp Suite can verify the token is encrypted and the hash is correct. Such tools will provide you with a basic report, which you then must analyze carefully. 3. Excessive Data Exposure The next type of vulnerability is related to the fact that APIs can return more data than is displayed by the UI. That is, data filtering takes place on the user interface and not on the back end. For example, you have an interface that displays three fields: First Name, Position, Email Address, and Photo. However, if you look at the API response, you may find more data, including some sensitive data like Birth Date or Home Address. The second type of Excessive Data Exposure occurs when UI data and API data are both returned correctly, but parameters are filtered on the front end and are not verified in any way on the back end. You may be able to specify in the request what data you need, but the back-end does not check whether you really have permission to access that data. Again, you have to test for Excessive Data Exposure manually. Unfortunately, nothing but some ordinary tools and your skills will help you here. You need to analyze what data each API returns and see if it returns more data than necessary, and you must give unique scenarios the proper forethought. For an example of Excessive Data Exposure, consider the vulnerability found in GitLab. They had an API called Events API that returned a lot of data in response while filtering on the UI. Less data was displayed on the UI, and more sensitive data could be accessed on the API. 4. Lack of Resources & Rate Limiting The next vulnerability type is associated with insufficient resources and rate limits. Improper rate limiting is a type of vulnerability that occurs when an API has no limit on the number of requests it sends to another API or a server. A simple strategy to control this is to establish that each API should not send more than N requests per second. However, this strategy is not quite correct. If your client generates more traffic than another client, your API should be stable for all clients. This can be resolved using special status codes, for example, 429 (Too Many Requests). Using this status code, you can implement some form of Rate Limiting. There are also special proprietary headers. For example, GitHub uses its X-RateLimit-*. These headers help regulate how many requests the client can send during a specific unit of time. The second scenario is related to the fact that you may not have enough parameter checks in the request. Suppose you have an application that returns a list of user types like size=10. What happens if an attacker changes this to 200000? Can the application cope with such a large request? To find rate limiting vulnerabilities, you can use different fuzzing tools like JBroFuzz or Fuzzapi. Or, you can use the same tools with which you analyze traffic. 5. Broken Function Level Authorization The next vulnerability is concerned with vertical levels of authorization —the user attempting to gain more access rights than allowed. For example, a regular user trying to become an admin. To find this vulnerability, you must first understand how various roles and objects in the application are connected. Secondly, you must clearly understand the access matrix implemented in the application. For example, say you have a project related to advertising. Your application has different entities like products, users, channels, and so on. You also have five options to view the list of ads, create ads, delete ads, update ads, and get information about each specific ad. These functions require different access rights. Therefore, the application has a user, authenticated user, manager, and admin. How can you find bugs and vulnerabilities in this situation? You could test sending a request to delete an ad while logged in as a regular user (instead of a manager) to see what the API returns. If the API returns 403 (Forbidden), then everything is fine. However, if the API returns two-hundredth codes like 200 (OK), 204 (No Content), or 201 (Created), then you have violated the access control matrix in some way. A similar vulnerability was found in Steam several years ago when a potential attacker was able to pull out game activation keys. The researcher who found this vulnerability was experimenting and sending requests while logged in as a user. After experimenting with various parameters in the request field, he happened to use a “0” — suddenly, the Steam service exposed the entire list of game keys. 6. Mass Assignment The next type of vulnerability is called Mass Assignment. Many modern development frameworks try to make developers’ lives easier by providing convenient mass assignment functions (when assigning parameters in bulk). For example, say you have a piece of code where there is a User object that is assigned all parameters passed from the API or UI: def signup @user = User.create(params[:user]) # => User<email: “firstname.lastname@example.org”, password: “12345”, is_administrator: false> end As you can see above, you can pass parameters email and password. However, you also have is_administrator, which, in theory, should be assigned automatically or somehow formed on the back end. If an attacker can perform an HTML injection and set the is_administrator: true, when this code is executed, it will be assigned together with the rest of the parameters. The correct implementation of this would be to assign each parameter separately. That is, if you want to take only email and password, take only email and password and explicitly indicate this. Doing so does equate to many similar lines of code with different parameters, adding complexity for developers. Thankfully, new frameworks are simplifying this burden, thereby reducing this type of vulnerability. 7. Security Misconfiguration The next type of vulnerability is related to the misconfiguration of your web servers or API. What can you test here? First of all, unnecessary HTTP methods must be disabled on the server. Do not show any unnecessary user errors at all. Do not pass technical details of the error to the client. If your application uses Cross-Origin Resource Sharing (CORS), that is, if it allows another application from a different domain to access your application’s cookies, then these headers must be appropriately configured to avoid additional vulnerabilities. Any access to internal files must also be disabled. There are special security headers, like Content-Security-Policy, that you can also implement in your applications to increase the security level. 8. Injections In the OWASP top 10 web application security risks, injections take the first place; however, injections hold the eighth place for APIs. In my opinion, this is because modern frameworks, modern development methods, and architectural patterns block us from the most primitive SQL or XSS injections. For example, you can use the object-relational mapping model to avoid SQL injection. This does not mean that you need to forget about injections at all. Such problems are still possible throughout a huge number of old sites and systems. Besides XSS and SQL, you should look for XML injections, JSON injections, and so on. You can test for injections using different tools. For example, ReadyAPI provides a paid tool for automatic scanning. Others, like Burp Suite, are partially free. Or, if you use Postman on a project, you could perform basic injection tests using Postman and data-driven testing. 9. Improper Assets Management The next type of vulnerability is related to the mismanagement of your assets. This flaw is growing as engineers adopt DevOps, continuous testing, and CI/CD pipelines. From a security standpoint, it is essential to configure these CI/CD pipelines correctly. CI/CD pipelines have access to various secrets and confidential data, such as accounts used to sign the code. Ensure you do not leave hard-coded secrets in the code and don’t “commit” them to the repository, no matter whether it is public or private. When using virtual machines, containers may be created by the CI/CD pipeline, and microservices may be placed in a separate container. Throughout this process, make sure you don’t have old containers hanging around that everyone has forgotten about — these could easily become additional access points. 10. Insufficient Logging & Monitoring The last type of vulnerability has to do with insufficient logging and monitoring procedures. The main idea here is that whatever happens to your application, you must be sure that you can track it. You should always have logs that show precisely what the attacker was trying to do. Also, have systems in place to identify suspicious traffic, and so on. Again, OWASP provides detailed checklists to reference to ensure your application is protected. The DevSecOps Approach All vulnerabilities discussed above represent the result of insufficient measures or measures that were taken too late. You could hire two, five, ten security experts, run your security test before each release, and hope you don’t find critical vulnerabilities that force you to re-architect, do global refactoring, and then run tons of regression tests. However, today, more and more companies are trying to move security (and all testing) to the left in the development lifecycle. When we start thinking about security from the very beginning of our development process, we call this DevSecOps. With a DevSecOps mindset, we analyze functional requirements in terms of security. We set additional security requirements for our application. We review our deployment diagrams, architectures, any other diagrams in a 4+1 architectural view model. We model threats and build scenarios where we think about what types of vulnerabilities may appear in the architecture, infrastructure, or application, and we try to predict and prevent them. We scan the code even before it gets into the branch master. There are special static code analyzers that check our developers’ code and the code of third-party libraries. After that, you can do a security test using the API tools mentioned above. Your engineers must carefully check all configurations of containers, clouds, CI/CD pipelines and avoid the API vulnerabilities mentioned above. It is necessary to conduct frequent training for the project team. It’s also recommended that, once a year, you involve external companies to perform penetration testing to ensure you didn’t miss anything. All developers and testers should be aware of new vulnerabilities as they appear and regularly learn how to approach security correctly. DevSecOps is a difficult approach that requires a lot of time, resources, and knowledge, but this is the only way to protect your applications properly. API Security Brings Unique Concerns API testing requires a certain mindset. Testers should be prepared for the fact that they may not have a UI. They should be prepared to design different server requests from scratch and understand request and response headers. Documentation isn’t always up to date. Therefore, it’s always better to monitor real traffic to know how an application behaves. API testers have to constantly ask themselves questions like, “What if there are other versions of this API? Will the API differ, for example, for a mobile application and the web?” If testers are truly interested in protecting their APIs, they should cultivate this type of security mindset.