API Pentesting and Best Practices

Paul De Baldo V
Senior Technical Engagement Manager
Image
Digital Background

Over the years, developers have adopted a modular approach to building web applications by leveraging application programming interfaces (APIs). While APIs have allowed application providers to extend their capabilities and integrate third-party services, their widespread use has attracted cybercriminals seeking to gain unauthorized access to sensitive data.

 

To harden your APIs against attacks, HackerOne offers a methodology-driven penetration testing (pentesting) solution delivered via a Pentest as a Service (PTaaS) model. This approach connects organizations with a heavily vetted cohort of a global ethical hacker community for comprehensive, end-to-end pentesting. Frequently performing dedicated pentesting, using a community-driven PTaaS is crucial to finding vulnerabilities in your APIs.

Testing Methodologies

HackerOne’s API testing methodologies are grounded in the principles of the PTESOWASP API Top 10 and CIS Controls. Additionally, our testing processes adhere to the standards required for CREST certification/accreditation, ensuring comprehensive and reliable assessments across the attack surfaces of your APIs. Organizations can now better protect against risk and attacks with highly skilled experts with specialized, proven expertise in vulnerabilities specific to APIs.

 

Our methodology is continuously evolving to ensure comprehensive coverage for each engagement. This approach stems from:

  • Consultations with both internal and external industry experts.
  • Leveraging and adhering to recognized industry standards.
  • Gleaning insights from a vast array of global customer programs, spanning both time-bound and continuous engagements.
  • Detailed analysis of millions of vulnerability reports we receive through our platform (see the Hacktivity page for details).

 

Threats are constantly evolving, so our methodology can’t remain stagnant. HackerOne’s Delivery team, including experienced Technical Engagement Managers (TEMs), constantly refine and adapt based on feedback and real-world experiences, delivering unparalleled security assurance.

Common Vulnerabilities

Broken Object/Function Level Authorization

In the context of APIs, an object refers to any piece of data or resource that the API serves, such as a user profile, a database record, or document. Object level authorization is the process of checking whether the authenticated user has the permission level required to access an object, while function level authorization checks verify they are allowed to perform an operation on an object. When this process is broken, threat actors may be able to obtain or modify data that is not intended for them, potentially exposing sensitive information or allowing unauthorized changes to occur.

 

Broken object level authorization vulnerabilities can be exploited by what are known as insecure direct object reference (IDOR) attacks. In these attacks, unintended data is returned by simply changing the identifying value tied to an object.

 

For example, a request sent to an API endpoint could fetch a user's order history for authenticated members. A GET request to https://example.com/api/invoices/3 returns the following response body:

 

{
    "order_id": 3,
    "user": {
        "email": "johndoe@example.com",
        "number": "5555555555"
    },
    "product": {
        "id": 1,
        "name": "keychain",
        "price": "10.00"
    },
    "quantity": 1,
    "status": "delivered",
    "transaction_id": "f8f73174-9428-4801-8f76-1c1dd44befe0",
    "created_on": "2024-11-18T21:51:54.047100",
    "payment": {
        "amount": 10,
        "paid_on": "2024-11-18T21:17:23.289220",
        "card_number": "XXXXXXXXXXXX5555",
        "card_owner_name": "John",
        "card_type": "Visa",
        "card_expiry": "01/2030",
        "currency": "USD"
    }
}

 

In the absence of sufficient authorization checks, the same user could send https://example.com/api/invoices/2 receive the details of an order made by another user:

 

{
    "order_id": 2,
    "user": {
        "email": "admin@example.com",
        "number": "3333333333"
    },
    "product": {
        "id": 2,
        "name": "keychain",
        "price": "10.00"
    },
    "quantity": 2,
    "status": "delivered",
    "transaction_id": "cb9cd837-5c8e-4617-acd0-12bce642269e",
    "created_on": "2024-11-18T21:51:54.047100",
    "payment": {
        "amount": 10,
        "paid_on": "2024-11-18T21:17:23.289220",
        "card_number": "XXXXXXXXXXXX3333",
        "card_owner_name": "Admin",
        "card_type": "MasterCard",
        "card_expiry": "10/2025",
        "currency": "USD"
    }
}

 

If a function level authorization vulnerability is present, API endpoints intended for use by high-privileged users may be accessible by low-privilege users. For example, https://example.com/admin/accounts/delete used by administrators to remove accounts may be unprotected. In this case, a threat actor could execute the associated function by navigating to this endpoint and supplying an arbitrary user id.

 

{
  "id": "555"
}

 

In some cases, function level authorization can be bypassed by changing the HTTP method of the request. For example, if the server only accepts POST requests to a certain endpoint that were made by an administrator, it may still be possible to execute the function using a GET request with the data as query parameters instead.

Broken Authentication

While authorization checks verify a user is allowed to perform a certain action, authentication checks verify a user is who they claim to be. In addition to general authentication exploitation techniques such as brute force and password spraying attacks, there are attacks commonly carried out against APIs such as token forgery and JSON Web Token (JWT) attacks.

 

In order to create a stateful relationship between a client and the server, tokens will be issued after a user supplies valid credentials. These tokens will be included in subsequent requests in order to identify which user they pertain to. However, any issues in how they are generated, processed, or handled can lead to vulnerabilities. For example, using tools such as Burp Sequencer, the quality of randomness in tokens can be evaluated. If tokens are discovered to be low-entropy, it is likely that valid tokens can be forged by fuzzing the characters in the index positions that are randomized.

 

JWTs are commonly used in APIs due to their interoperability across a variety of programming languages. However, poor implementations and configurations of JWTs can be abused in order to gain unauthorized access. These types of tokens consist of three parts, each separated by a period, and base64-encoded:

 

header.payload.signature

Header - The header includes information about the type of token and the hashing algorithm used to sign it.

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

Payload - The payload holds the data included within the token. The fields vary by API but usually contain user specific information and a timestamp (IAT). Each key of the key-value field pair is referred to as a "claim".

{
  "id": "333",
  "name": "johndoe",
  "iat": 1516239022,
  "role": "member"
}

Signature - The signature is generated by taking the base64 encoded header and payload, concatenating them with a period, and then applying the specified hash function in the header using a secret key. The resulting hash digest is then base64 encoded again. The secret key can be a password or secret string such as a 256-bit key value.

HMACSHA256(
      base64UrlEncode(header) + "." +
      base64UrlEncode(payload),
      secret)

 

If the JWT is using "none" as its algorithm, the server may accept tokens that have no signature at all. If this is the case, a malicious attacker could simply change claim values to assume the identity of another user or elevate their permission levels.

 

A vulnerability can also arise when a JWT accepts multiple algorithms. For example, RSA256 is an asymmetric encryption algorithm. If this is being used, a threat actor would need to obtain both the public and private key in order to hash the signature. However, if the JWT also accepts HS265, it may be possible to hash the signature using just the RSA256 public key.

 

Brute force attacks can also be carried out in an attempt to discover the value of the secret key. These attacks can be performed offline, meaning rate limiting protections will not prevent them. By supplying a wordlist of possible values, an attacker can generate a vast quantity of tokens in a short amount of time and then compare them to the original token.

Mass Assignment

Mass assignment vulnerabilities occur when an end user is allowed to supply additional parameters to a request that are accepted by the server. Once accepted, the extra parameter will be set in the object on the backend.

 

Parameters that could possibly be added can be enumerated through evaluation of normal web traffic or may even be disclosed in documentation. Threat actors could even guess valid parameters based on naming conventions or fuzz for them with premade wordlists.

 

For example, if a request discloses a parameter that specifies the permission role of a user, if the API is vulnerable to mass assignment a user could include it in a profile update request to achieve privilege escalation:

 

PATCH /api/users/137 HTTP/1.1
Host: example.com
Authorization: Bearer 123abc321xyz

{
  "username": "johndoe",
  "email": "johndoe@example.com",
  "role": "administrator"
}

 

This parameter could even be included to create a new account with administrator privileges:

 

POST /api/users/register HTTP/1.1
Host: example.com
{
  "username": "johndoe",
  "email": "johndoe@example.com",
  "password": "qwerty"
  "role": "administrator"
}

Excessive Data Exposure

Excessive data exposure occurs when API endpoints return more information than necessary. For example, if a user object includes an array of other user objects representative of their platform friends that discloses detailed information about them as well:

 

{
  "id": "137",
  "firstName": "John",
  "lastName": "Doe",
  "username": "johndoe",
  "email": "johndoe@example.com",
  "phoneNumber": "+1 333-333-3333",
  "role": "member",
  "friends": [
    {
      "id": "138",
      "firstName": "Jane",
      "lastName": "Doe",
      "username": "janedoe",
      "email": "janedoe@example.com",
      "phoneNumber": "+1 555-555-5555",
      "role": "administrator"
    },
    {
      "id": "139",
      "firstName": "David",
      "lastName": "Lee",
      "username": "davidlee",
      "email": "davidlee@example.com",
      "phoneNumber": "+1 888-888-8888",
      "role": "member"
    },
    {
      "id": "140",
      "firstName": "Emily",
      "lastName": "Jones",
      "username": "emilyjones",
      "email": "emilyjones@example.com",
      "phoneNumber": "+1 222-222-2222",
      "role": "member"
    }
  ]
}

 

This over-fetching of data is why the GraphQL API query language was developed. However, without protective measures such as query cost calculations, excessive data exposure can still occur by nesting query fields. Additionally, GraphQL APIs are an additional attack surface that need to be secured.

Injection Attacks

APIs can also be compromised by traditional injection attacks. For example, if there is no input validation, sanitization, or either of the schemas are insufficient, an attacker could alter the database queries in a SQL injection attack:

 

POST /api/login HTTP/1.1
Host: example.com

{
  "username": "admin",
  "password": "password' or 1=1;--"
}

 

Under normal conditions, when a user is not using SQL language syntax, the login form will check the credentials against the database using:

 

SELECT * FROM users WHERE username = '$username' AND password = '$password';

 

However, with the addition of ' or 1=1;--, which will always evaluate to true, the attacker will be able to login to the admin account without having to know the password.

 

SELECT * FROM users WHERE username = 'admin' AND password = 'password' or '1'='1';

Best Practices

Careful Scoping

Having the right scope is crucial to a successful pentest—what is being tested can be just as important as how it is being tested. APIs can be complex, with a variety of endpoints, authentication mechanisms, and integrations. Combining API security testing with both internal network and web application penetration testing offers a comprehensive security assessment. This integrated approach provides pentesters with a holistic view of the environment, leading to more effective and thorough results.

 

By strategically selecting APIs, specific endpoints, and services to target, you can ensure quality time is spent focusing on the most critical assets of your system. This curation can mean the difference between an inconsequential security test and a valuable assessment that discovers high-impact vulnerabilities. HackerOne evaluates your API assets in order to provide guidance on which ones to include and delivers a quote tailored to your specific requirements.

Skills-Based Tester Matching

Traditional consultancies often rely on in-house pentesters with general skills. However, API security testing requires specialized knowledge of API vulnerabilities, the underlying mechanisms and the unique risks associated with API-based communication.

 

With HackerOne, customers gain access to a diverse pool of elite, vetted security researchers who bring a wide range of skills, certifications, and experience. The HackerOne platform tracks each researcher's skill set based on their track record and matches the most suitable researchers for each engagement. The community-driven PTaaS approach delivers comprehensive coverage, versatility, and the highest-quality results tailored to the security needs of your API endpoints and services.

Case Study: Taking Control of 15 Million Vehicle Devices

In an article published by security researcher Sam Curry, the details of multiple API vulnerabilities within the automotive industry are disclosed. Included is the step-by-step process Curry and his team executed in order to gain remote control over millions of cars.

 

Spireon, a company that provides GPS tracking solutions for vehicle fleet management, had an SQL injection vulnerability in their administrative portal which could be exploited to bypass the authentication process. The working injection payload that allowed anyone to assume the administrative account was simply: ' #.

 

The unauthorized access granted the research team extensive functionality including the ability to force a vehicle to download malware and track the live GPS location of a vehicle – including those belonging to police departments and hospitals. A backend API endpoint was also discovered and bypassed using %0d carriage returns. This bypass allowed the team to escalate the severity of the finding as they now were able to install backdoors, query ownership information, and send fleet management invites to themselves.

Why HackerOne Is The Best Option For API Security Testing

By choosing HackerOne as your partner in pentesting, your organization can fully benefit from the community-driven PTaaS model. This model leverages a combination of HackerOne security experts, who are skill-matched and vetted, working together with your teams to deliver the best overall ROI in risk reduction.

The HackerOne Platform simplifies the process of requesting a new pentest, onboarding new assets, and enlisting expert researchers in just a few days. Its purpose-built UI for reporting vulnerabilities and Zero Trust Access for fast, secure application access makes pentests more seamless and efficient.

With the right blend of people and technology, HackerOne is the ideal choice for your API pentests.