Contact Us Today 01642 716680

API Security Testing: Examples, Vulnerabilities, Mitigation

In this blog post, we dive deep into the common api vulnerabilities identified through API Security Testing. What are the common vulnerabilities? How can you mitigate issues with your API? To find out, read on.

Introduction to API Security Testing

In this blog post, we explore the topic of API Security Testing and provide real-world examples, including code snippets and attack scenarios. API security is a vast topic involving many components that an organisation needs to understand before pushing an API to production. If you are a developer looking to understand API vulnerabilities in further detail, this blog post is for you.

What is API Security Testing, and why is it important?

API security testing is the process of evaluating an API to detect security vulnerabilities. This usually involves various test techniques, such as penetration testing, fuzzing for specific vulnerabilities, and code reviews. API security testing aims to understand if an API can withstand several malicious activities and function securely within its intended environment. Incorporating API security testing is highly worthwhile and can save organisations money from costly data breaches; using security testing techniques, developers can identify and fix any issues before they become exploitable by an attacker and enhance the overall security posture of the application.

Rest API security is a huge talking point in web application security, mainly because APIs have become the backbone of modern applications. Many companies now develop and deploy APIs, so their security has become more critical than ever. APIs enable communication between services and devices. However, with more connections, we often see more vulnerabilities. Implementing robust API Security Testing practices ensures your assets are safe from attackers.

API Security Testing (OWASP Top 10 API)

The image shows common API Security Testing vulnerabilities, the list details the top ten issues as compiled by OWASP.

This section will explore the most critical API security risks identified by OWASP API Security Top 10 and provide real-world examples to illustrate each point. If you prefer to get these details at a glance, look at our downloadable PDF.

Broken Authorisation Controls

First on the OWASP top 10 list is “Broken Object Level Authorisation” controls. APIs often expose endpoints that handle object identifiers, making them susceptible to unauthorised data access if not properly secure. This risk occurs when the server doesn’t correctly verify whether a user is authorised to access a specific object. So, very similar to web application authorisation controls, users should only be able to access resources their permission level allows.

Example of Broken Authorisation Controls:

An example would be an e-commerce API where users can view order details, such as for an endpoint like /api/orders/[orderid].

Code example for “Broken Authorisation Controls” (Node.js):

app.get('/api/orders/:orderId', (req, res) => {
  const orderId = req.params.orderId;
  Order.findById(orderId, (err, order) => {
    if (err) return res.status(500).send(err);
    if (!order) return res.status(404).send('Order not found');
    res.json(order);
  });
});

While this code might seem valid to the layperson, it allows any user to access any order by specifying the order without verifying that the order belongs to the user requesting it.

If the API doesn’t check whether the order ID belongs to that authenticated user, then an attacker could manipulate that order ID parameter to access someone else’s order information.

Attack scenario for “Broken Authorisation Controls”:

If an attacker discovers this vulnerability, they could browse the orders of previous users and extract sensitive information from the endpoint.

HTTP request (Example request exploiting the above code):

DELETE /api/orders/4556 HTTP/1.1
Host: vulnerableapi.com

Mitigation strategies for authorisation vulnerabilities

  • Implement Role-Based Access Controls (RBAC)
  • Ensure authorisation controls are correctly designed and rely on user policies and hierarchy.
  • The user of random and unpredictable values, such as GUIDs for record identifiers.
  • Ensure that appropriate tests are conducted to evaluate the security of the authorisation controls.
  • Consult the guidance provided by OWASP on “Broken Authorisation Controls”, which is accessible here.

Broken Authentication

Next on the list, we have “Broken Authentication”. As we’ve seen with web applications in the past, authentication is a vast topic and requires a blog post in itself. Authentication is a critical component of securing any digital asset; however, to discuss this briefly within the context of REST API Security, authentication flaws can often allow an attacker to assume another user’s identity, or it could allow an attacker to ultimately compromise several accounts simultaneously. Authentication flaws happen when the authentication mechanisms are particularly weak or improperly implemented.

Example of Broken Authentication:

For example, imagine an API that uses JSON Web Token’s JWT for authentication but doesn’t validate the token signature or expiration time correctly. If the API accepts tokens without verifying their cryptographic signature, an attacker could create a forged token with any user ID and gain unauthorised access to accounts. While becoming less common, this has happened on sensitive assets, such as the “NHS COVID-19 Android app“. Alternatively, suppose an API sets tokens to expire far in the future or not at all. If a token user’s token was compromised during that period, perhaps through a man-in-the-middle attack, an attacker could use that token indefinitely to access a user’s account.

Code example for “Broken Authentication” (Node.js, Express):

function authenticateToken(req, res, next) {
    const token = req.headers['authorization'];
    if (!token) {
        return res.status(401).send('Access denied');
    }
    // Decode token without verifying signature
    const decoded = jwt.decode(token);
    req.user = decoded;
    next();
}

This code decodes the JWT without verifying its signature or expiration, which could allow an attacker to force a token.

Example Attack Scenario for Broken Authentication (Lack of JWT signature verification):

Using the above code example, if an attacker wanted to gain access to another user’s account without valid credentials, they could forge a JWT token and authenticate as that user. JWT tokens are structured with three main components: a header, payload, and signature, which are separated by a “.” They are then subsequently Base64 Encoded.

Example JWT Token:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJ1c2VyIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Breaking down the token:

  • eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 is the header.
  • eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ is the payload
  • SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c is the signature.

An attacker could forge a token by encoding their header and payload and omitting the signature.

Header (With ‘none’ algorithm):

{
"alg": "none",
"typ": "JWT"
}

Payload (Impersonating a user with the ID 12345):

{
"userId": "12345",
"role": "admin"
}

The attacker would then encode the JSON data using Base64 encoding:

Header: eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
Payload: eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJhZG1pbiJ9

Combining them without a signature:

eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJhZG1pbiJ9.

The attacker could then send a request to a protected endpoint, impersonating a user:

GET /api/protected-data HTTP/1.1
Host: vulnerableapi.com
Authorisation: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiIxMjM0NSIsInJvbGUiOiJhZG1pbiJ9.

Another common authentication vulnerability issue is a lack of an account lockout policy. So if an API’s login endpoint doesn’t have an account lockout policy and allows unlimited login attempts, attackers could use an automated tool to perform credential stuffing attacks, trying thousands of username/password combinations until they find a match.

Mitigation strategies for authentication vulnerabilities

  • Proper JWT token validation is critical. The JWT signature must be validated using a secure algorithm, such as HS256 or RS256, and the correct secret or public key.
  • The token’s expiration claim (the ‘exp’) and other standard claims like issuer (the ‘iss’) or audience (the ‘aud’) must be verified. All tokens that fail validation or have been tampered with should be rejected.
  • Enforce strong password policies by mandating minimum password length and complexity requirements.
  • Prevent commonly breached passwords by checking them against compromised password lists, such as those provided by services like Have I Been Pwned.
  • Perform API Security Testing with a reputable provider.
  • Rate limiting should also be implemented on authentication endpoints to slow down repeated login attempts by a would-be attacker. Accounts should be temporarily locked after a certain number of failed login attempts, and the user should be notified of the lockout.
  • Consult the guidance provided by OWASP on “Broken Authentication”, which is accessible here.

Broken Object Property Level Authorisation

This vulnerability category focuses on the lack of authorisation validation controls at the object property level, which could lead to information disclosure or manipulation by unauthorised people. Broken object property-level authorisation essentially occurs when an API doesn’t enforce checks on properties within an object. This vulnerability relates to “Mass Assignment,” which was previously in the top ten list provided by OWASP. Mass Assignment can be difficult to understand, likely due to how it is titled, but it is worth looking into and understanding if you are developing an API.

While a user might be authorised to access or modify a specific object, they might not have permission to access or modify particular properties. Failing to validate user permissions at the property level can lead to access or manipulation of sensitive data.

Example of “Broken Object Property Level Authorisation”:

Imagine an application that allows users to update their profile information through an API endpoint. The user profile object includes properties such as username, email, password, and role. Regular users should be able to update their username and email but not their role.

Code example for “Broken Object Property Level Authorisation” (Node.js with Express):

app.put('/api/users/:userId', authenticateUser, (req, res) => {
    const userId = req.params.userId;
    const updateData = req.body;

    User.findByIdAndUpdate(userId, updateData, { new: true }, (err, user) => {
        if (err) {
            return res.status(500).send('Server error');
        }
        if (!user) {
            return res.status(404).send('User not found');
        }
        res.json(user);
    });
});

This vulnerable code allows the user to update the properties without actually validating which of the properties can be updated. Therefore, a user could update their role to something such as “admin.”

Example HTTP Request for the above attack scenario:

PUT /api/users/12345 HTTP/1.1
Host: vulnerableapi.com
Authorization: Bearer ...
Content-Type: application/json

{
    "username": "newUsername",
    "email": "newemail@example.com",
    "role": "admin"
}

Mitigation strategies for property level authorisation vulnerabilities

  • Define which properties are allowed. Only update specific properties based on the user’s role and/or permission level.
  • Always verify the user permissions level before allowing changes to sensitive properties.
  • Do not assign user input to data models without proper validation.
  • Consult the guidance provided by OWASP on “Broken object property-level authorisation”, which is accessible here.

Unrestricted Resource Consumption in REST API Security

Next, we have Unrestricted Resource Consumption, more commonly understood as a lack of API Rate Limitations. When an API accepts a substantial amount of user input without proper limitations, it risks overwhelming the back-end system, leading to excessive consumption of server memory, CPU, and storage resources. Ensuring REST API security involves checking for unrestricted resource consumption that could lead to a denial of service.

Many developers are now choosing to utilise cloud resources for file upload storage, such as AWS or Azure storage, however, it is still common enough for developers to store files locally on the web-server.

Example of “Unrestricted Resource Consumption”:

Let’s take, for example, an API that processes image uploads but doesn’t have size restrictions on the upload. If an attacker were to send thousands of extremely large images within a short time (say 30 seconds), it could consume excessive resources.

Code example for “Unrestricted Resource Consumption”:

app.post('/api/upload', (req, res) => {
    const file = req.files.file;
    if (!file) {
        return res.status(400).send('No file uploaded');
    }
    res.send('File uploaded successfully');
});

Another scenario involves data entry endpoints. An attacker might generate numerous invalid or malicious entries by sending thousands of requests with dummy data. This strains server resources and can corrupt back-end data, disrupt analytics, and interfere with the application’s normal operations.

Mitigation strategies for resource consumption vulnerabilities

  • Implement effective rate limitations, restricting the number and size of requests a client can make at once or within a short period.
  • Payload size limits should also be configured to a maximum allowed value for uploads and request bodies.
  • Configure monitoring and alerting to detect unusual spikes in resource usage.
  • Consult the guidance provided by OWASP on “Unrestricted Resource Consumption”, which is accessible here.
  • Consult the guidance on rate-limiting for whichever web framework you are using. Such as Django, Laravel, Flask and so on.

Broken Function Level Authorisation

Next, we have broken function-level authorisation. This vulnerability arises when an API doesn’t correctly enforce authorisation checks on specific functions or endpoints. It can also allow attackers to access functionalities they wouldn’t normally be able to. This vulnerability is typically seen when an API has overly complex hierarchies, multiple roles, or simply oversights in implementing proper authorisation checks. Attackers have been known to exploit this by sending legitimate API calls to endpoints they’re not authorised to access.

Example of “Broken Function Level Authorisation”:

Imagine there’s an application with both regular users and administrator users. The app’s admins have special access to endpoints that allow them to perform actions such as managing user accounts, deleting content or accessing sensitive data that only the admin should be able to see. Suppose the API does not properly enforce the authorisation checks on these admin endpoints.

Example code for “Broken Function Level Authorisation” (Node.js with Express):

app.delete('/API/users/:userId', (req, res) => {
    const userId = req.params.userId;

    User.findByIdAndDelete(userId, (err) => {
        if (err) {
            return res.status(500).send('Server error');
        }
        res.send('User deleted');
    });
});

In the code above, any authenticated user (Or even an unauthenticated user) can send a DELETE request to the API endpoint /api/users/:userId and delete any user account.

Attack scenario for “Broken Function Level Authorisation”:

When an attacker discovers that the endpoint lacks functional-level authorisation controls, they fuzz it and delete all the application’s valid users. Fuzzing the application would be straightforward using a tool such as Burp Suite Intruder; the attacker would simply increment the user identifier (In this case, sequential numbers) and proceed to delete all the users on the system.

HTTP request for the above attack scenario:

DELETE /api/users/12345 HTTP/1.1
Host: vulnerableapi.com
Authorisation: Bearer attackerToken

Mitigation strategies function level authorisation vulnerabilities

  • Ensure that all endpoints perform authorisation checks; the user should have the necessary permissions to call the function.
  • Assign roles to users (guest, standard, admin etc) and restrict access to the endpoints base on the roles.
  • Ensure the authorisation controls are enforced at the code level for all endpoints.
  • Consult the guidance provided by OWASP on “Broken Function Level Authorisation”, which is accessible here.

Unrestricted Access to Sensitive Business Flows

Now, unrestricted access to sensitive business flows is a bit vaguely titled. Still, it intends to encompass vulnerabilities that arise when an API exposes some business function without appropriate restrictions. This category of vulnerability can allow an attacker to exploit a function in a way that could harm the business. What kind of business flows is this referring to? It could be accessing and purchasing products, making reservations, or posting comments.

Example of “Unrestricted Access to Sensitive Business Flows”:

Consider an online ticketing platform that sells concert tickets through it’s API. The API exposes a specific endpoint that allows the end-users to purchase tickets.

Example request for “Unrestricted Access to Sensitive Business Flows”:

POST /api/tickets/purchase HTTP/1.1
Host: vulnerableapi.com
Content-Type: application/json

{
    "eventId": "concert123",
    "quantity": 2,
    "paymentDetails": {
        "cardNumber": "4111111111111111",
        "expiryDate": "12/25",
        "cvv": "123"
    }
}

Attack scenario for “Unrestricted Access to Sensitive Business Flows”:

An attacker wants to purchase all the available tickets for a high-demand concert (Take the recent Oasis tour example) and resell them at a higher price. As the API doesn’t have appropriate protections to stop this exploit, the attackers automate the requests and purchase a good portion of the available tickets.

Mitigation strategies for sensitive business flow vulnerabilities

  • The first step is to identify all sensitive business actions that must be protected.
  • Consult the guidance provided by OWASP on “Unrestricted Access to Sensitive Business Flows”, which is accessible here. https://owasp.org/API-Security/editions/2023/en/0xa6-unrestricted-access-to-sensitive-business-flows/

Server-Side Request Forgery and API Security Testing

Server-side Request Forgery is appropriately titled. It describes what it is: the server is manipulated into sending a request to an unintended destination (or a destination the attacker is trying to reach). So, instead of coming from the client side, the request is coming from the server side. Exploiting SSRF could have significant consequences. It could allow an attacker to bypass firewall rules and access internal resources that wouldn’t be accessible from the external perspective.

Example of “Server-Side Request Forgery”

Let’s explore a real-world example using the EC2 metadata endpoint and discuss possible attack scenarios. First, a little background on the EC2 metadata endpoint. In AWS, EC2 instances have access to an endpoint that provides metadata about the instance, including sensitive information such as IAM roles (http://169.254.169.254/latest/meta-data/iam/security-credentials/). This endpoint is accessible only from within the instance and should never be exposed externally. Surprisingly, this EC2 data retrieval example is a common enough example to mention. If you are interested in diving deeper into SSRF issues and looking through some common examples, HackTricks SSRF is a great resource.

Looking at an example attack scenario, imagine an API endpoint that fetches the contents of a specific URL provided by the user and then returns the response.

Example code for “Server Side Request Forgery” (Node.js with Express):

app.get('/api/fetch', (req, res) => {
    const url = req.query.url;

    // No validation on the URL
    fetch(url)
        .then(response => response.text())
        .then(data => res.send(data))
        .catch(error => res.status(500).send('Error fetching URL'));
});

If we look at the code, the API takes the URL parameter from the query string, uses the fetch function to retrieve the content at that URL, and then returns it to the client without any validation. An attacker could exploit this vulnerability to access an EC2 metadata endpoint and get the server to retrieve that information.

Example attack scenario for “Server Side Request Forgery”

The attacker sends a request to the vulnerable endpoint, and the endpoint retrieves the information from the meta-data endpoint and supplies this to the attacker. The attacker gains access to the IAM credentials, which they can then use to access AWS services with whatever permissions are applied to the role (Using the AWS CLI).

Example HTTP request:

GET /api/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/ HTTP/1.1
Host: vulnerableapi.com

Mitigation strategies for SSRF vulnerabilities

  • Only allow URLS from trusted domains or IP addresses.
  • Use allow lists for remote origins, url schemes/ports and accepted media types.
  • Consult the guidance provided by OWASP on “Server Side Request Forgery”, which is accessible here.

Security Misconfigurations in REST APIs

Now we have another title, which is also appropriately titled, security misconfiguration. Security misconfiguration is pretty broad in scope, and it occurs when specific security settings are not correctly implemented and configured across the whole API technology stack. This can lead to vulnerabilities that attackers can exploit.

We’ll discuss what security misconfiguration entails, an example. Security misconfigurations could include outdated patches for specific systems the API is utilising, unnecessary features left enabled, default credentials, or even verbose error messages from the API.

Example of a “Security Misconfiguration”:

To give an example scenario, let’s consider the code base contains a hard-coded string that configures the connection to mongodb database:

const mongoose = require('mongoose');
mongoose.connect('mongodb://admin:admin@localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true,
});

In this example code, we have a database connection that uses the credentials admin:admin for MongoDB, an easy combination for an attacker to guess. An attacker could gain unauthorised access to the database using these default credentials, potentially leading to accessing or modifying a whole bunch of sensitive data.

Mitigation strategies to limit security misconfiguration vulnerabilities

Mitigating security misconfigurations is very broad, requiring examining the whole technology stack and hardening each specific area.

  • Network Level considerations:
    • Configuring firewalls and security groups only to allow necessary traffic, applying a deny-all approach and working backwards from here.
    • Properly segment the network and isolate sensitive systems.
  • Server Level considerations:
    • Disable unnecessary services, ports and protocols.
    • Ensure that default accounts are disabled and default passwords are changed to secure passwords.
    • Keep on top of patching operating systems and server technology.
  • Application level considerations:
    • Ensure that debug mode has been disabled and verbose error messages.
    • Use secure configurations for application servers and frameworks, following the best practices
  • Consult the guidance provided by OWASP on “Security Misconfigurations”, which is accessible here.

OWASP also provides guidance and examples on “Improper Inventory Management” and “Unsafe Consumption of APIs,” which are worth reading; however, the topics are relatively broad. It is worth looking at the content provided by OWASP to familiarise yourself with the issues discussed. However, regular API Penetration Testing must be included to address these areas and enhance overall REST API Security.

Conclusion

REST API Security is a broad topic involving many technology stack components. Securing the code base is essential; however, many developers struggle to implement all the required changes to their environment before pushing them to production. While there are many steps a developer can take to mitigate vulnerabilities, the best way to identify issues is to perform thorough API Security Testing with a reputable provider. If you need API penetration testing, please get in touch with us using our contact form.

What are the OWASP Top 10 API Security Risks: Download our flash cards to find out.

Inside you will find a description of the most common API vulnerabilities.