DEV Community

Cover image for GraphQL a security disaster waiting to unfold
Thomas Hansen
Thomas Hansen

Posted on • Originally published at aista.com

GraphQL a security disaster waiting to unfold

A couple of days ago I wrote an article here called GraphQL a hot smoking pile of garbage. In the article I was arguing that GraphQL was nothing but "JSON based SQL injection attacks". Everybody arguing against me told me "this is not how GraphQL is being used, you need to study how it's used, your opinion is based upon faulty assumptions, etc". Well, take a look at the above architectural sketch and feel free to explain. The above image basically proves how GraphQL is a security disaster.

The above architectural sketch is one of the primary use cases used by the second largest GraphQL service provider in the world. I don't even want to disclose their name, because of security reasons, since the above architectural sketch is basically a direct implementation of the problems I was illuminating in my previous article.

The service provider have roughly 35,000 stars on GitHub, they have 60 million dollars in VC fundings, and they're not even the largest service provider in this space. There's another similar company with 75,000 stars on GitHub, that encourages their users to do the same. And according to a website I saw the other day, there are 88 alternatives to the largest company, and 54 alternative to the second largest company in this domain.

My article wasn't written for Facebook or GitHub. I have no doubts that both Facebook and GitHub knows how to correctly apply GraphQL. My article was written for the rest of the world. The 98% of software developers applying GraphQL, believing it's an "easy alternative to HTTP REST and business logic on the server".

The above use case published by the service provider is for a FinTech company, with 3,500 users. I can only pray to (insert deity of choice) that they're not PCI compliant. Facts are ...

GraphQL a security disaster

If you need an alternative that's secure by default, feel free to register a free CRUD API generator cloudlet here.

Oldest comments (12)

Collapse
 
polterguy profile image
Thomas Hansen • Edited

@joelbonetr

Combined these two vendors has 120,000 stars on GitHub ... :/

And they have 130 similar copy cat companies delivering similar products ... :/

My guess is that 98% of all GraphQL implementations are done like the above sketch illustrates ...

Collapse
 
joelbonetr profile image
JoelBonetR πŸ₯‡ • Edited

Hi Thomas, GraphQL schemas for a service are specified using what's known as the GraphQL SDL (Schema Definition Language), Schema-First methodology is also the most common thingy in this context.

GraphQL has 3 categories of operations, i'm adding them with the http equivalent to showcase:

  • Query (GET)
  • Mutation (POST, PATCH, PUT, DELETE)
  • Subscription (Websockets, SSE)

In each type of request that you want to handle, there are different parts. A definition on what it contains and a definition on what it returns.

The operations are performed through Resolvers, see example below:

const resolvers = {
    Query: {
        getAllUsers() {
            // do whatever you like
            return users;
        },
        getUser(parent, args) {
            // do whatever you like, example:
            const user = users.find(u => u.id === parseInt(args.id));
            if (!user) throw new UserInputError(`The user ${args.id} was not found`);
            return user;
        }
    },
    Mutation: {
        createUser(parent, args) {
        // do whatever you like
       ...
Enter fullscreen mode Exit fullscreen mode

It's up to you how to resolve each request. It's usually one of two things:

Request -> Resolver -> Req Validation -> ORM
or
Request -> Resolver -> Endpoint (which can be a REST req and this endpoint will also implement a Req Validation and a call to an ORM)

Of course the ORM is optional but as it's the most common I'm sticking with this flow.


Please note that GraphQL itself it's just a definition (which is the model you want to expose, how you are going to resolve the requests and what will you return as response) thus it has nothing to do with security alone.

The security, as well as you do on a regular http endpoint, is up to you. The framework, libs, handlers and functions need to be arranged in a way that prevent security vulnerabilities. Usually the framework and the ORM have standard functions/methods to deal with common security concerns, you can use dedicated libs for each topic if you prefer so.

GraphQL is not "an easy alternative to HTTP", it has it's pros and cons (yet most of them have been tackled by now) like any other tech and yes, both have a common point in utility (resolve requests through TCP/UDP, multiplexed or not). Depending on what you need to build it will be easier or harder, more or less convenient or a better or worse choice using a tech or another.

I don't want to convert this comment in an entire post explaining what GraphQL is and how it works, what architectures are suitable for it, what it does and what it don't and going to the detail but I assume that with the information yet provided it should suffice to showcase.

Collapse
 
polterguy profile image
Thomas Hansen • Edited

You're probably 100% perfectly correct. However, every single person arguing against me said "this is not the way GraphQL is intended to be used", at which point I said "this is how 98% of the world is using GraphQL". We've now got proof of that I was right ... ;)

Me being right of course, doesn't imply you're not right ...

BTW, having schemas is a terrible idea, since it moves the important parts out of the parts where the action is. We had similar problems with Hibernate and nHibernate before they were able to create "fluent APIs", with AOP type of syntax, coupling the "configuration" with the code, making it easier to see things and maintain ...

Thread Thread
 
joelbonetr profile image
JoelBonetR πŸ₯‡ • Edited

The same way when you create a RESTful endpoint you define the request handlers (for each http method) and then you add the validations and the resolvers for it, it's not so different.

Generally speaking, those are different topics and maybe GraphQL could work well together with your CRUD generator (that's another topic to discuss) as Gateway to provide an easy I/O point (optional) for your programmatically generated CRUDs.

I don't see GraphQL as competitor on your field but a "nice to have", as it's explained above, GraphQL does not generate any CRUD itself, it just provides a standardized way to handle client requests through an SDL and quite useful notations, rather than fighting with OpenAPI specs trying to define something that works not only for now but for the future. See GraphQL Directives as example.

having schemas is a terrible idea, since it moves the important parts out of the parts where the action is.

If you understand the schema as and it's workflow as the place where the action is you may change your mind 😁
Take it a try, do some different PoCs and maybe you will find a way to incorporate GraphQL in your system, being optional or not at the end it's TBD.

Everything on this topic can be summarized in a sentence; It's not what reaches your server but how do you handle it.

Thread Thread
 
polterguy profile image
Thomas Hansen

I once had a dev guy once spending 2 weeks trying to configure Pulsar. We've known since RoR days the following ...

Prefer convention over configuration ... ;)

Thx, I don't disagree with you really, and you've got some great points - However, the way 98% of the world for all practical concerns is using GraphQL is the exact opposite that commenters were commenting on my OP about ... ;)

If you don't believe me, look at the architectural sketch on the top of this article, and go back to my OP and see peoples' arguments ...

Thread Thread
 
joelbonetr profile image
JoelBonetR πŸ₯‡ • Edited

I've never saw a production ready GraphQL project whose resolvers are attached to a database model directly without security layers. That's because GraphQL itself doesn't provide a way to do that, to reach this you need to code your resolvers in that specific way.

On the other hand, if you use GraphQL just as gateway (which is quite convenient), you don't need to secure your GraphQL for each resolver, the resolver itself (another endpoint) will catch the responsibility for that (just like any other GW does).
Nevertheless if it's the case, it's convenient as well to handle Auth in the gateway so you avoid requests to other endpoints if the request will fail for that reason anyway.

TLDR;

  • You can go ahead without extra security in GraphQL if the implementation is not resolving the requests directly on the place, delegating the specific security to each service/microservice/endpoint (just like you do in REST, SOAP or anything else).
  • If you need Authentication, you should add Auth management and security in the gateway to avoid doing requests to other services that will fail anyway. That is true as well for any architecture that implies a gateway (as far as I can think of now).
    The schema you provided: Image description

States just that. The Backend, that can be GraphQL or not, handles the request and the Authentication.
Then resolve each request with whatever is needed (that include integrations with other services, being third party or not).

In this specific case, the backend seems a single service without pieces in between the client and the backend itself. It's OK if it works for the target.

You can find architectural schemas in many different system designs using GraphQL:

  • It can be a monolithic block in which you'll need to handle the security:
    Image description

  • It can be a GraphQL gateway and the security and the DB access it's used in each stand-alone service:
    Image description

  • Maybe a combination of both:
    Image description

  • And tones of options more, depending on the complexity of the system.

You can add proxies, load balancers, more than one gateway (BFF architecture), services and/or microservices that are using GraphQL themselves, others using REST, others using gRPC and so on.

Also you can have a monolith that exposes a GraphQL I/O, another one using REST and another one using gRPC so your thingy can be integrated with most clients out there and they can use their preferred method. The same way you can have a services or microservices architecture and provide 2, 3... 25 gateways if you want.

At the end, GraphQL it's just a piece of the entire puzzle, that you can use or not.
It's up to you to analyse and define what system design suits best for the target of your App (or a given product line or a product adaptation for a given market niche) and which tools are needed, convenient and nice to have so you can plan your roadmap better and give your clients the best possible service.

Collapse
 
jackmellis profile image
Jack

I don't particularly like graphql myself, I much prefer rest schemas with prescribed models.

On the other hand, after reading this and your previous article on the topic, and your replies to people in said articles, it certainly comes across like you're a "my way or the highway" kinda person with a big ego who loves pointing out how many likes and follows they have.

Disappointing because I was hoping for an unbiased argument about graphql with well-documented evidence that supported my initial opinion.

Collapse
 
polterguy profile image
Thomas Hansen • Edited

Disappointing because I was hoping for an unbiased argument

You could try to argue factually instead of doing an ad hominem ...?

Just sayin' ... :/

It would certainly increase your credibility ...

I would at least ensure we had something to actually discuss at least ... ^_^

Collapse
 
jmfayard profile image
Jean-Michel πŸ•΅πŸ»β€β™‚οΈ Fayard

Indeed I don't get this one. Any API that provides direct read/write access to a database is stupid. That stupidity can be implemented with GraphQL or REST or SOAP or whatever, the technology doesn't matter.

If you want to rename GraphQL, statically typed REST is the right answer.

Collapse
 
polterguy profile image
Thomas Hansen • Edited

When the client is constructing the queries, it's (almost) impossible to prevent it from constructing malicious queries. The client is "user land", implying anybody with a Postman account can create queries. Even if you get all authorisation objects correctly applied towards the different tables you've got, you're still left with problems such as those originating from batch queries, where your GraphQL server ends up "DOS attacking" your database, creating a "query storm", plus a bajillion additional problems.

There are ways to secure GraphQL, by for instance not allowing the client to create queries, but rather constructing the queries in your middleware, and exposing statically types endpoints to the client. The problem is that most aren't using GraphQL this way, they see GraphQL and start thinking; "Cool, 'server less'", and implements all business logic on the client, allowing for creating queries in "client land".

The second problem is that even if you're able to somehow completely secure GQL, you're still ending up with a solution that for all practical concerns moves all business logic into client space. Typically the same API have multiple clients. You're now risking that each client implements its business logic differently, and/or users bypassing your business logic with their Postman accounts, resulting in "bad data".

The problem with using GraphQL this way is that it reduces your middleware to a "fancy database thing". It hence solves nothing really, but only "moves" the problem to an outer layer.

Most people commenting here claims they're not using GraphQL this way, as in exposing the query building directly to the client. This is probably true. However, most people commenting here aren't representative of how GraphQL is used by most developers.

However, it's the idea of GraphQL that's the problem, because if you give people a big shotgun, most people are going to shoot themselves in their legs. As a library developer I've learned one thing, which is to restrict usage of a library as much as possible. Even though I can perfectly apply the library, most others cannot, and will abuse it in ways it wasn't intended to be used ...

The above screenshot has a GraphQL vendor in the parts which are censored. This implies that the only thing that's between the database and the frontend client is a GraphQL server. This is the only product this particular vendor happens to have, and we must hence assume 98% of its clients are using GraphQL like this. To further emphasise that, realise that this particular vendor is using this as their "best practice use case" in their marketing. This particular vendor has 35,000+ stars on GitHub. There's another similar vendor with 70,000+ stars on GitHub, delivering the exact same product ...

The architectural sketch above is a use case from a FinTech provider. This company have 3,500 users. One of them might be you. If they're PCI compliant, they've got your credit card information. I consider this a really big deal ... ;)

Here's the first paragraph from GraphQL's primary website. They're basically summing up the problem a hundred times better than I can do myself.

GraphQL

If you use GraphQL like GraphQL's website tells you to do, you're in a "security disaster land", because everyone with a Postman account can construct Graph queries you hadn't anticipated ...

It's basically one step up from allowing the client to pass in raw SQL queries ...

Collapse
 
jmfayard profile image
Jean-Michel πŸ•΅πŸ»β€β™‚οΈ Fayard • Edited

The problem with using GraphQL this way is that it reduces your middleware to a "fancy database thing". It hence solves nothing really, but only "moves" the problem to an outer layer.

We agree that's stupid.

The issue is that it's hard to discuss if we use the same word for two completely different concepts.

There is "GraphQL A" which means "giving direct acess to the database using graphql as an excuse".

Then there is "GraphQL B" which means "the alternative to rest from facebook that actually has little to do with graphs but is important nonetheless because it introduces a proper contract between backend and frontend based on static types" (the usual definition).

When the client is constructing the queries, it's (almost) impossible to prevent it from constructing malicious queries.

Not in GraphQL B. It's exactly as easy or hard as in REST, because graphql is mainly rest with static types, and static types are not a relevant factor here. Like I'm litteraly using the same code to serve an endpoint via Rest and GraphQL.

you're still ending up with a solution that for all practical concerns moves all business logic into client space

In GraphQL A yes.

For GraphQL B, what changes is that it gives the fronend some reasonable flexibility such as not receiving the fields it doesn't need, or grouping multiple requests in one http query. The business logic is still in the backend though, very reasonable.

you're still left with problems such as those originating from batch queries, where your GraphQL server ends up "DOS attacking" your database,

GraphLQ B has a small difference with REST here, because the attacker could do that in one graphql query instead of multiple rest queries. But it isn't that big a difference, writing a script that DDOS with multiple REST queries is super easy and super common too.

So you have to implement rate-limiting and batch loading either way.

However, it's the idea of GraphQL that's the problem, because if you give people a big shotgun, most people are going to shoot themselves in their legs. As a library developer I've learned one thing, which is to restrict usage of a library as much as possible. Even though I can perfectly apply the library, most others cannot, and will abuse it in ways it wasn't intended to be used ...

The idea of GraphQL B is sound, it doesn't give any shotgun that was not there before but it does gives a very useful tool: a contract betwen front and back with static types.

Companies hyping GraphQL A, companies that provides or uses an automagical "direct access to your postgres database via graphql" do however and deserve to be shunned, and the more the better. Here I have no problem agreeing with you.

Thread Thread
 
polterguy profile image
Thomas Hansen

There is "GraphQL A" which means "giving direct acess to the database using graphql as an excuse".

The problem is that most companies are using this version ... :/

And the reason is because it's literally how GraphQL is being marketed. Both by the creators of GraphQL, and hundreds of companies setting up companies being service providers providing this as their sole service.

The idea of GraphQL B is sound, it doesn't give any shotgun that was not there before but it does gives a very useful tool: a contract betwen front and back with static types.

I agree, but see my comment above ...

do however and deserve to be shunned

Most developers don't see the difference. The particular vendor you're talking about is according to their own account "the fastest growing open source project on the planet". They've got 70,000+ stars on GitHub ...

To create an omelet you sometimes have to break a couple of eggs. Hopefully, people using such tech will search for GraphQL the next time before they start using it, and find this article, saving them from security holes the size of Niagara falls ...