Juice-shop: GraphQL Injection

Created on 12 Jun 2017  ·  4Comments  ·  Source: bkimminich/juice-shop

https://www.npmjs.com/package/graphql

Is injection a thing for GraphQL?


Want to back this issue? Post a bounty on it! We accept bounties via Bountysource.

challenge wontfix

Most helpful comment

Brace yourself this is quite a long Post 😅

I've done some development work with GraphQL in last few month.
While looking into it I couldn't find any research or material on this topic.
So i did some thinking and tried a few things out, the following is probably not nearly complete but should provide some overview of some possibilities of injection type attacks vectors and defenses in GraphQL.

TLDR: Injections are theoretically possible, but probably not a real concern for most real world applications (unless they are doing some weird shit).

To show this a short example how a GraphQL Query and Request would look like.

First a short example how a normal GraphQL Request and a possible injection might look like, using the most boring example of a GraphQL Query fetching blog posts and their author.

query {
    post(id: "some-id") {
        title
        text
        author {
            name
        }
    }
}

This query would normally get send from the client to the server via ajax.
The GraphQL APIs is typically hosted on a single http post endpoint located at /graphql.

The API would then return the data as json in the form which was requested by the query. This would look something like this:

{
  "data": {
    "post": {
      "title": "Injections for GraphQL?!",
      "text": "Lorem Ipsum...",
      "author": {
          "name": "Jannik Hollenbach"
       }
    }
  }
}

In this query there is one dynamic part, which could be used by the attacker. That is the the post id (“some-id”).
To perform a injection a really neat part of GraphQL can be used, which is the ability to send multiple queries to the server in one request.

This could be used to insert some statements. Assuming that the argument is just inserted by string concatenation. (More on that later 😉)

A attacker could for example link to a blog post with the following id:

some-id") {
    name
}
user {
    credid_card
}
post(id: "some-id

This would execute the following complete query on the clients browser:

query {
    post(id: "some-id") {
        name
    }
    user {
        credid_card
    }
    post(id: "some-id") {
        title
        text
        author {
            name
        }
    }
}

This could absolutely work and could be used to fetch sensitive information about the current user. (Hopefully the blog would not actually store credit card information 😅)

So, why isn’t this a real problem?

Unfortunately for the attacker the attack / request would be performed by the clients browser. All information returned by the injected query is “trapped” in the browser and probably discarded by the application as the app doesn’t care about data it wasn’t really asking for.

To get the Data out the attacker would need to perform some sort of XSS attack. Which makes such injections into GraphQL useless to begin with, as the attacker could've just used the injected XSS Code to perform these queries.

Also GraphQL has some defenses against these sort of things.

Remember when i mentioned i will come back to injecting data into the query by using string concatenation?

GraphQL has apparently learned from the early mistakes of SQL and comes with a way of separating user data and query from each other without having to concatenate any strings or escape something.

This works by defining variables on a top level of the query and then referencing these variables:

query PostQuery($id: String!) {
    post(id: $id) {
        title
    }
}

The query will be send to the server, usually in json, with the query and variable data separated:

{
    "query": "query PostQuery($id: String!) { post(id: $id) { title } }",
    "variables": {
        "id": "some-id"
    }
}

This should, as far as i can see, prevent all injections.

Assuming that the developer messed this up, could the attacker use injections to change stuff, e.g. delete Blog Posts?

I think this possibility is the most interesting one, as this has pretty clear similarities to the way XSRF attacks are normally used as both are used to send requests to the server to modify stuff the user didn't want to modify.

And technically yes, it could be used in such way.

But again another concept in GraphQL makes this unlikely, as long as the developers followed some of most basic guidelines of the language.

This is the distinction between queries and mutations.

Queries are used to fetch stuff, while mutations are, well… used to change stuff. The difference between query and mutation is mostly conceptual. So assuming that some application are getting it wrong seems plausible.

GraphQL Queries seem to behave a bit like XML Documents as they always need a root type which is normally either query or mutation. Mixing these in a single request appears to be impossible in my limited time I tried to do it.

There is a way to send both queries and mutations in one request, but its really unlikely to inject something into it.
To understand this, lets take a look how a typical GraphQL Request looks like on the http side.

Payload (JSON) of a GraphQL Request. This typically is a POST Request send to a /graphql http endpoint.

{
    "query": "query { post(id: \"some-id\"){ title } }"
}

Note: The “query” key in the json payload is a bit confusing as mutations are also send in there.

A mutation would look something like this:

{
    "query": "mutation { deletePost(id: \"some-id\") }"
}

Apollo Server which seems to be the most used GraphQL Server supports to send multiple different operations in one request by putting the json document in an array.

[
    {
        "query": "query { post(id: \"some-id\"){ title } }"
    },
    {
        "query": "mutation { deletePost(id: \"some-id\") }"
    }
]

While this would allow an attacker actually run mutations, there would need to be some really weird client code to allow a injection like this. This could only happen when the developer tries to implement their own GraphQL client library instead of using one of the commonly used (and really cool abstractions) like Apollo Client.

Summary

Yes it is possible. But there needs to go a lot of things wrong before this could be a potential problem.

On the topic of GraphQL related challenges:

I would say GraphQL related challenges should probably wait for some more research in security threats faced for a broader spectrum of GraphQL APIs, because the threats i came up with seem a bit weird 😅.

All 4 comments

This might be of interest:
hackerone blogpost

Brace yourself this is quite a long Post 😅

I've done some development work with GraphQL in last few month.
While looking into it I couldn't find any research or material on this topic.
So i did some thinking and tried a few things out, the following is probably not nearly complete but should provide some overview of some possibilities of injection type attacks vectors and defenses in GraphQL.

TLDR: Injections are theoretically possible, but probably not a real concern for most real world applications (unless they are doing some weird shit).

To show this a short example how a GraphQL Query and Request would look like.

First a short example how a normal GraphQL Request and a possible injection might look like, using the most boring example of a GraphQL Query fetching blog posts and their author.

query {
    post(id: "some-id") {
        title
        text
        author {
            name
        }
    }
}

This query would normally get send from the client to the server via ajax.
The GraphQL APIs is typically hosted on a single http post endpoint located at /graphql.

The API would then return the data as json in the form which was requested by the query. This would look something like this:

{
  "data": {
    "post": {
      "title": "Injections for GraphQL?!",
      "text": "Lorem Ipsum...",
      "author": {
          "name": "Jannik Hollenbach"
       }
    }
  }
}

In this query there is one dynamic part, which could be used by the attacker. That is the the post id (“some-id”).
To perform a injection a really neat part of GraphQL can be used, which is the ability to send multiple queries to the server in one request.

This could be used to insert some statements. Assuming that the argument is just inserted by string concatenation. (More on that later 😉)

A attacker could for example link to a blog post with the following id:

some-id") {
    name
}
user {
    credid_card
}
post(id: "some-id

This would execute the following complete query on the clients browser:

query {
    post(id: "some-id") {
        name
    }
    user {
        credid_card
    }
    post(id: "some-id") {
        title
        text
        author {
            name
        }
    }
}

This could absolutely work and could be used to fetch sensitive information about the current user. (Hopefully the blog would not actually store credit card information 😅)

So, why isn’t this a real problem?

Unfortunately for the attacker the attack / request would be performed by the clients browser. All information returned by the injected query is “trapped” in the browser and probably discarded by the application as the app doesn’t care about data it wasn’t really asking for.

To get the Data out the attacker would need to perform some sort of XSS attack. Which makes such injections into GraphQL useless to begin with, as the attacker could've just used the injected XSS Code to perform these queries.

Also GraphQL has some defenses against these sort of things.

Remember when i mentioned i will come back to injecting data into the query by using string concatenation?

GraphQL has apparently learned from the early mistakes of SQL and comes with a way of separating user data and query from each other without having to concatenate any strings or escape something.

This works by defining variables on a top level of the query and then referencing these variables:

query PostQuery($id: String!) {
    post(id: $id) {
        title
    }
}

The query will be send to the server, usually in json, with the query and variable data separated:

{
    "query": "query PostQuery($id: String!) { post(id: $id) { title } }",
    "variables": {
        "id": "some-id"
    }
}

This should, as far as i can see, prevent all injections.

Assuming that the developer messed this up, could the attacker use injections to change stuff, e.g. delete Blog Posts?

I think this possibility is the most interesting one, as this has pretty clear similarities to the way XSRF attacks are normally used as both are used to send requests to the server to modify stuff the user didn't want to modify.

And technically yes, it could be used in such way.

But again another concept in GraphQL makes this unlikely, as long as the developers followed some of most basic guidelines of the language.

This is the distinction between queries and mutations.

Queries are used to fetch stuff, while mutations are, well… used to change stuff. The difference between query and mutation is mostly conceptual. So assuming that some application are getting it wrong seems plausible.

GraphQL Queries seem to behave a bit like XML Documents as they always need a root type which is normally either query or mutation. Mixing these in a single request appears to be impossible in my limited time I tried to do it.

There is a way to send both queries and mutations in one request, but its really unlikely to inject something into it.
To understand this, lets take a look how a typical GraphQL Request looks like on the http side.

Payload (JSON) of a GraphQL Request. This typically is a POST Request send to a /graphql http endpoint.

{
    "query": "query { post(id: \"some-id\"){ title } }"
}

Note: The “query” key in the json payload is a bit confusing as mutations are also send in there.

A mutation would look something like this:

{
    "query": "mutation { deletePost(id: \"some-id\") }"
}

Apollo Server which seems to be the most used GraphQL Server supports to send multiple different operations in one request by putting the json document in an array.

[
    {
        "query": "query { post(id: \"some-id\"){ title } }"
    },
    {
        "query": "mutation { deletePost(id: \"some-id\") }"
    }
]

While this would allow an attacker actually run mutations, there would need to be some really weird client code to allow a injection like this. This could only happen when the developer tries to implement their own GraphQL client library instead of using one of the commonly used (and really cool abstractions) like Apollo Client.

Summary

Yes it is possible. But there needs to go a lot of things wrong before this could be a potential problem.

On the topic of GraphQL related challenges:

I would say GraphQL related challenges should probably wait for some more research in security threats faced for a broader spectrum of GraphQL APIs, because the threats i came up with seem a bit weird 😅.

On this matter it would be interesting if this workshop from the owasp summit last year made any findings: https://owaspsummit.org/Working-Sessions/Research/GraphQL-Security-Review.html

Does somebody know something about it?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

agrawalarpit14 picture agrawalarpit14  ·  4Comments

junathanadi picture junathanadi  ·  3Comments

toptuto picture toptuto  ·  7Comments

bkimminich picture bkimminich  ·  5Comments

bkimminich picture bkimminich  ·  6Comments