I feel like this thread is tongue-in-cheek to make people realize that "run any query on the client side" is a terrible idea, and that's exactly what most GraphQL installations offer.
As a penetration tester though I like GraphQL, it's a very handy data exfiltration engine. By default, there's no authorization, no authentication, and it even comes with introspection. One query and I get your entire schema! This trendy movement will be far worse for data security than SQL injection, unsecured S3 buckets, or misconfigured NoSQL instances.
> As a penetration tester though I like GraphQL, it's a very handy data exfiltration engine. By default, there's no authorization, no authentication, and it even comes with introspection. One query and I get your entire schema! This trendy movement will be far worse for data security than SQL injection, unsecured S3 buckets, or misconfigured NoSQL instances.
All of that has nothing to do with GraphQL though, it's just poor security. You'd have the exact same issue with classic REST, SOAP, or whatever protocol one is using. I imagine the issue is that people are using "convenience" frameworks on top of their GraphQL libraries that do the glue between their databases and GraphQL and these frameworks don't have security as a priority.
Development-wise I'd argue that the less endpoints, the more secure, but it doesn't mean an app should have the exact same GraphQL endpoint for both its admin and its user facing data.
The principle of the least privilege will always apply along with a rigorous security audit, code audit and third party lib audit.
It has a lot to do with GraphQL the ecosystem, though, doesn't it?
SQL injection is a particularly bad problem because concatenating strings is the easy path to getting queries working, and parameterization takes (a tiny bit) more effort.
Similarly, a system such that "by default, there's no authorization, no authentication, and it even comes with introspection" is a system that makes it very easy to do the wrong thing. And the default settings of GraphQL generators tend to be even worse than that.
> SQL injection is a particularly bad problem because concatenating strings is the easy path to getting queries working, and parameterization takes (a tiny bit) more effort.
Also there was a rocky period in the 90s where support for parameters was shaky or slower, which meant entire communities (PHP, ASP, even Java) established a fair amount of shared practice not doing that. Like PHP’s decision to ignore errors and register variables globally, it took many years to get that fixed in tutorials, open source code, bad API design, etc.
Yeah my first encounter with GraphQL was a client going "let's save development time by using graphql!" so I looked into it and it's more fucking work unless your stack has one of those auto-GraphQL-to-and-from-DB generators (they'd made a poor and somewhat obscure choice of DB for the project, over our advice, so there wasn't such a thing in this case anyway) and it happens to suit your needs very well—and even with the benefit of one of those I'd still lose sleep over security or DoS bugs lurking in that 3rd party code. Or in mine.
The cases where GraphQL make sense are the ones where the front end is making tons of intricate, complicated calls. If it’s a simple REST api you’re interacting with it probably doesn’t need GraphQL and might actually be harmed by it.
GraphQL is only interesting for flexibility to fast prototyping/changes on the client side, to me. And it should stay there. Meaning your client JS should have a data layer that can understand GraphQL and translate that to a REST API call and cache the result.
The only extra work compared to REST is writing your query and mutations input and output types. But since any serious REST API nowadays includes swagger, it's actually even if not worse on REST.
Then it's all the same as of how much work. In the server, each REST endpoint is a gql resolver.
As of auto-generation regards: yes, gql maps great to a relational model, so you can save a lot of effort going that route. But you know, there's auto-generation for REST setups too.
- Swagger is not the only API Spec framework. I think rather than serious REST APIs having Swagger, good APIs have (auto-generated) client libs.
- in REST, you have no default way of querying data related to the current path. Of course, you can use frameworks for that (filtering, hydrating references, etc.). In GraphQL, you have entry queries and the rest is graph traversal, and that is basically the default (and of course, it does come with its disadvantages)
- on auto-generated schema: the interesting thing here is not whether you have auto-generated schemas etc. at all, but how well the data relations can be surfaced, explored and accessed on the API level. GraphQL enabled this in a different way compared to REST, and I would dare say it is a more delightful way.
As a client, given a Swagger API Documentation and a GraphQL Schema, I would prefer exploring the GraphQL Schema, just to find out what the API is about.
I agree with this. Working with protobuf and generating the TS/PB files for the client/browser on a Go project felt like this. With some better tooling around that I think it would be a really slick development environment.
While I have also seen many poorly secured GraphQL API:s, this seems like an unfair criticism. To me GraphQL is mostly an alternative to REST, which also has no authorization or authentication by default, that is a orthogonal concern, but you can hardly blame REST for that.
I'm building a service for finding a gym buddy (on hold now) and leveraging exposed endpoints has been great for getting gym locations for various chains. Much better than screen scraping.
Introspection and query building make client apis easy and quick to develop. In production deployments, you can limit introspection and persisted queries are used to ensure known query loads and characteristics. This lets you iterate a lot faster on getting the right set of apis vs trying to do this type of evolution with rest endpoints.
Part of the point is also to weave a cohesive tree of information from possibly diverse data sources, and it’s incredibly good for that.
Actually GraphQL was the thing that made me reconsider if running queries on the client-side was a bad idea or not, which was one of the reasons I started seriously experimenting with client-side SQL.
I absolutely agree that you gotta be extremely careful about security though. And in my experience on large enough teams someone's going to mess that up at some point, so that's a big risk in any client-side querying mechanism.
At least AWS Amplify/AppSync offers authorization schemes down to fields. I had the impression that was the norm, but good to know. I will pitch that in the future.
"Run any query on the client side" with no authorization, authentication or introspection is exactly what SPARQL public endpoints offer. It's a feature, not a bug.
So swagger is bad because "one request and I get your entire schema"?
> run any query on the client side" is a terrible idea
that's a big mischaracterization. A typed query language that lets you tailor your queries a la carte doesn't remotely mean the queries "run on the client side".
No, because in Swagger the first step is carefully designing exactly what the inputs and outputs are of each and every API call. Most GraphQL servers you simply point it at a database and it automatically makes your entire schema, including tables and fields that shouldn't be exposed to end users, readily available.
> doesn't remotely mean the queries "run on the client side".
Sorry, that was poorly written. Obviously the queries are not running on the client side. My point was that the client can provide any query they want and the system will process it.
Most GraphQL servers you simply point it at a database and it automatically makes your entire schema, including tables and fields that shouldn't be exposed to end users, readily available.
You're arguing against auto-generated gql setups, not gql. And it's not even accurate. Anyone that makes that mistake in gql would make it in REST, SOAP or what have you.
Auto-generated REST setups are aplenty too, and lately auto-gen tools offer both gql + rest. PostgREST/PostGraphile. Hasura does both too.
> it even comes with introspection. One query and I get your entire schema! This trendy movement will be far worse for data security than SQL injection
Isn't this also true of SQL injection? Sure, you might need to run multiple queries to retrieve multiple schemas. But SQL injection is just a way to run arbitrary SQL commands, and you can do a lot with those.
You can avoid this by allowing only specific queries in staging and production environments, this way, the introspection query can be disabled too.
Not sure if this violates the GraphQL spec though, because, ideally the server supports introspection, but it is disabled in some cases for safety reasons.
But if your limiting to only a whitelist of queries what's the difference vs a standard API? You might as well then just have a REST endpoint with the query defined on the server if your only allowing certain queries. Get your Javascript dev's to write a Node service or equivalent with the query logic inside it; that way the query logic doesn't need to be replicated per client.
This is where GraphQL shines. You give your developer a schema and "they" choose the queries and mutations they want to make to make their life easy. Also, there's very less friction because the GraphQL schema can be completely documented.
And while going to prod, you just allow the queries that are being used, disable rest.
Yep, you definitely need to implement user authz for _every_ resolver indepedently and safelist/allowlist only those operations you'll use. You probably had to disable introspection in production too.
Out of curiosity, could you name the third party security firm?
I didn't work on the security audit implementation though. I actually forgot what security firm we used, but it was local in our area.
They provided a document detailing all the security exploits they found though.
I don't recall exactly how this is done in graphQL, but i believe we used the context object and made a request to our database to find the users role. GraphQL endpoints have 4 arguments, the 4th one specifies the datagraph payload coming in. I think we blacklisted everything and whitelisted them depending on what the user requested and their corresponding role.
As a penetration tester though I like GraphQL, it's a very handy data exfiltration engine. By default, there's no authorization, no authentication, and it even comes with introspection. One query and I get your entire schema! This trendy movement will be far worse for data security than SQL injection, unsecured S3 buckets, or misconfigured NoSQL instances.