Share on

How threat actors fingerprint your GraphQL APIs

Shahar Binyamin & Inigo team·

In previous blog posts, we discussed what’s involved in the process of detecting GraphQL servers. Threat actors don’t stop there, the identification of a target is only the initial step in the process of achieving the greater goal of compromising a GraphQL API target.

You’ve identified a GraphQL server, now what? Well, the next step in the process is to learn all you can about the specific server. You may be asking yourself, aren’t all GraphQL servers the same; they just provide an API layer to some application? While this is true, the first thing you should know is that there are many GraphQL server implementations, not all of them are mature, and not all of them are well maintained.

Let’s explore some of the GraphQL servers that are out there today. A good source for this is the website, which lists client libraries, servers, tools as well services by the language they were written in. For this post, we’re going to focus only on server implementations. Below chart illustrates the number of GraphQL server implementations available today by language. You can find the detailed table at the bottom of this post.

GraphQL Servers Breakdown.png

As you can see, there are many different server implementations. Most notable are PHP (22.8%) Golang (12.3%) JavaScript (10.5%) Java (8.8%) and Python (7%).

Why is this important? When threat actors target an application or server, they need to gather a few key data points to increase their chances of successfully breaking in:

  1. What platform is running?
  2. Is the source code available?
  3. What version is it on?
  4. Are there any known vulnerabilities for it?
  5. Are there any available exploits for the vulnerabilities?
  6. What security features are built into it?
  7. Are there any insecure settings shipped by default?

To be able to know the answer to these questions, you first need to fingerprint the target. But how do you fingerprint an API? Since GraphQL is just an API layer, you may be asking yourself: how would someone know what GraphQL implementation it is if they should all conform with the specification and return the same predictable response structure? Well, if you craft a careful enough payload that the server does not expect, all it will take is a small and subtle difference in the response to distinguish one server from another. Let’s explore what this looks like.

GraphQL Server Fingerprint.png

GraphQL supports three operation types: query, mutation and subscription as described in section 2.3 Operations in the GraphQL specification. What would happen if an operation that doesn’t exist is provided, and what would the server respond with? Let’s find out:

queryyy { # notice the typo

Using curl, we will send this malformed query to an Apollo-based GraphQL server

$ curl -s -X POST http://apollo.example.local -H "Content-Type: application/json" -d '{"query":"queryyy { __typename }"}' | jq

  "errors": [
      "message": "Syntax Error: Unexpected Name \"queryyy\".",
      "extensions": {
        "code": "GRAPHQL_PARSE_FAILED"

So, we have an errors key with a message of Syntax Error: Unexpected Name "queryyy". Now, let’s do the same against a Graphene-based GraphQL server, a Python-based implementation.

$ curl -s -X POST http://graphene.example.local -H "Content-Type: application/json" -d '{"query":"queryyy { __typename }"}' | jq

  "errors": [
      "message": "Syntax Error GraphQL (1:1) Unexpected Name \"queryyy\"
\n1: queryyy { __typename }\n   ^\n",
      "locations": [
          "line": 1,
          "column": 1

Now we see that the error message is a little different. For example, instead of Syntax Error we now get Syntax Error GraphQL in the error message. This is an obvious difference that can help us pinpoint the implementation.

Tools that perform GraphQL fingerprinting surfaced over the years, such as Graphw00f that make use of these subtle differences to arm ethical hackers with the necessary knowledge and give them the ability to perform more educated guesses during penetration tests.

We mentioned earlier that some implementations are more mature than others, and offer protections that others don’t. In future posts, we will cover various vulnerabilities of different implementations. Whether you are using a popular GraphQL server implementation, or completely wrote your own custom implementation, Inigo abstracts your GraphQL API layer and provides security protections so you don’t need to worry about securing your GraphQL server, we do that for you.

List of GraphQL server implementations available as of this writing

# Implementation Language URL
1 graphql-js JavaScript
2 apollo JavaScript
3 graphql-yoga JavaScript
4 express-graphql JavaScript
5 mercurius JavaScript
6 graphql-helix JavaScript
7 graphql-go Golang
8 gqlgen Golang
9 graphql-go (graph-gophers) Golang
10 thunder Golang
11 graphql-relay-go Golang
12 jaal Golang
13 eggql Golang
14 api-platform PHP
15 graphql-php PHP
16 WPGraphQL PHP
17 Lighthouse PHP
18 Siler PHP
19 GraphQLBundle PHP
20 GraphQLite PHP
21 Ralit PHP
22 graphql-relay-php PHP
23 GraphQL by PoP PHP
24 GraphQL API for WordPress PHP
25 GraPHPinator PHP
26 serge PHP
27 graphql-java Java
28 Domain Graph Service (DGS) Framework Java
29 graphql-kotlin Java
30 GraphQL Spring Boot Java
31 KGraphQL Java
32 graphql-dotnet C# / .NET
33 Hot Chocolate C# / .NET
34 NGraphQL C# / .NET
35 Graphene Python
36 Strawberry Python
37 Ariadne Python
38 Tartiflette Python
39 Graphiti Swift
40 GraphZahl Swift
41 Juniper Rust
42 Async-graphql Rust
43 graphql-ruby Ruby
44 Agoo Ruby
45 Absinthe Elixir
46 graphql-elixir Elixir
47 Sangria Scala
48 Caliban Scala
49 Iacinia Clojure
50 graphql-cli Clojure
51 alumbra Clojure
52 Morpheus GraphQL Haskell
53 Mu-Haskell Haskell
54 ocaml-graphql-server OCaml
55 graphql-erlang Erlang
56 ghql R
57 gorm-graphql Groovy
58 graphql-perl Perl
59 graphqld D
Ready to accelerate your GraphQL adoption?
Start Inigo for free
*No credit card needed
Join our newsletter