author: BBKing

Web API Testing

Overview

Web API testing is the same as Web App testing, except there’s no browser.

  • No web browser means no Javascript interpreter means no XSS.
  • No web browser also means CSRF is far less likely.
    • …but not impossible, especially if there’s a web app in the same domain.
  • Logic flaws are probably the most likely type of finding
    • Privilege escalation (horizontal and vertical)
    • Account recovery holes
    • Session-handling rules
  • CORS is more likely to be a factor in a web API than in a traditional web app

Quick Hits

  • Do the same ancillary things for Web APIs that you do for Web Apps.
    • Nikto, Nmap, Dirbuster, etc.
  • The main problem is that there’s no UI. Whereas you can learn most of what you need to know about a web app by clicking around, there’s no “thing” to click around in for an API.
    • Unless there is … some web apps are just a front-end to an API. In this case, you can learn about the API by using the web app.
    • Some APIs are published with default help interfaces. These help interfaces can be used to submit requests to the back-end API like a web app. It may be useful to check for these in the event that there is no web app.
  • If there’s a web app to go along with the web API, it’ll be one of three types:
    • The web app implements the entire API directly (and probably uses a client-side Javascript framework to do the rendering).
      • Test the web app thoroughly, and you’ve also tested the API.
  • The web app covers the same functionality, but by a different mechanism (e.g. HTTP GETs and POSTs with query-string formatted parameters … a “normal” web app)
  • The web app and the API are two different but very closely related things. They operate on the same data, but by different (maybe inconsistent) methods.
    • Use the web app initially to create objects that you use the API to interact with. (Later, use the API to create them - there may be different options available)
    • Test the two as a unit: the web app and the API will necessarily have different input validation rules, and may follow different business logic. Try to use one against the other.
  • The web app implements a subset of the API directly.
    • Use the web app as far as it is useful for populating things for the API.
    • Shift to the API for the parts that are not exposed in the web app.
    • As above, use the two against each other. Add a thing with the API and view it in the web app.
  • For setup, you want (tell this to your point of contact): “the same kind of setup the QA team uses for their testing, because testing for security is a kind of QA testing.”
    • The documentation generated by their framework (e.g. the Swagger file) isn’t always detailed or targeted enough.
      • Example: If the API lets you search for objects by their name, and the name is a string, then what specific string will return a useful object?
      • Example: If you add an “attachment” to a “support case” by the attachment’s GUID, then what GUIDs are appropriate?
    • You need some objects you can interact with
    • You need sample requests that show how you’re meant to interact with them.
    • You need sample responses that show what a “normal” response looks like.
    • You don’t need a sample of every possible request, although having it all may save you time. You need just enough to give you access to the objects you need in order to create every possible request (or, if the API is huge, every type of request, for some thoughtfully-chosen sample) yourself.
  • You can use Postman or SoapUI to read the WSDL or Swagger/OpenAPI spec and generate sample requests.
    • …then send those through Burp (and let them fail because the values in the requests aren’t right, but the syntax is).
    • …then use Burp as normal (send to Repeater, type in valid values for the parameters, send that, get a good response, send to Intruder or Scanner or etc)
  • You can also use BApp Store Apps to do the same thing from within Burp.
    • OpenAPI Parser (for Swagger / OpenAPI files in JSON or YAML)
    • Wsdler (for WSDL files in XML)
  • Attack the server-side parser
    • XXE, command injection, server-side template injection
    • Understand the business logic, then attack it
    • Try to access things you should not be allowed to access (e.g. other users’ data, admin functions as non-admin user, etc - the same approach as for any app)
  • Attack authentication
    • The login function
    • The “forgot my password” function
    • The “sign me up” function
  • Session tokens
    • Randomness (Burp Sequencer)
    • Which Bits Matter (Burp Intruder’s “Character frobber” payload)
    • Reasonable lifetime AND effective expiration
    • Embedded information (encoded usernames and roles)
  • Identify and attack JWTs, if used, same as in any app.
    • BApp Store has two things that help, here (TODO: find their actual names)
    • Try forging one with a null cipher
    • Try altering an existing one (to make sure the signature is verified)
    • See what’s in the payload (base64 decode the middle part). Maybe secrets here.
    • Put one up on the cracker to see if you can recover the password
      • …and if you do, forge one and tell us all about it!

Postman Tips

Pre-Request Scripts to Set Variables

Use a “pre-request script” to refresh auth tokens. Here’s one example where the response to the login POST contains a JWT called “idToken” inside a JSON blob – that JWT goes in an Authorization header in subsequent requests.

In those “subsequent requests” I have the Authorization header set to use an environment variable called idToken.

This pre-request script sends a login request, then sets the idToken variable in Postman to be whatever came back as the idToken’s value in the login response. There’s no error handling.

const loginBody = '
[{"username":"brian.king@example.org","password":"Example@test09","clientId":"beaddeefarar](mailto:%7B%22username%22:%22brian.king@example.org%22,%22password%22:%22Board@test09%22,%22clientId%22:%2265dvj)"}
'

const options = {
 'method' : 'POST',
 'url': "[https://example.org/users/login](https://example.org/users/login)",
 body: loginBody
}

pm.sendRequest(options, function (err, response) {
 console.log(response.json());
 var jsonData = response.json();
 pm.environment.set("idToken", jsonData.idToken);
});

Virtserver.swaggerhub.com is OK

If you see a “host” of “virtserver.swaggerhub.com” in your Swagger / OpenAPI definitions, it’s not a sign that they’re sending info to a third-party host. Well, it is, but they’re doing it on purpose, and the “third party” is SmartBear (maker of ReadyAPI and SoapUI)

That’s the default “mock” endpoint – if you send a request there, you’re supposed to get a valid (but static) response.

See more at https://support.smartbear.com/swaggerhub/docs/integrations/api-auto-mocking.html

Send Every Request in Swagger UI

Via Jack in Teams, March 5, 2024

// Run each of these, one at a time
// 1
const expandButtons = document.querySelectorAll('.opblock-summary-control');  
expandButtons.forEach(button => {  
    button.click();});
    
// 2
const TryItOut = document.querySelectorAll('.try-out__btn');  
TryItOut.forEach(button => {  
    button.click();});
 
/ 3
const Execute = document.querySelectorAll('.execute');  
Execute.forEach(button => {  
    button.click();});