4 ms·
For the love of god, this is never how GraphQL was intended to be used. The official graphql website is very clear: https://graphql.org/learn/authorization/ ht
by mooted1 7y ago
For the love of god, this is never how GraphQL was intended to be used. The official graphql website is very clear:
https://graphql.org/learn/authorization/ https://graphql.org/learn/authorization/
> Delegate authorization logic to the business logic layer
If you take security or performance debugging seriously, you should never expose database models through APIs directly in a production app.
To illustrate, say you have an Employee model:
query {
employee(userId=uuid) {
name
salary
}
}
Say you add some hacks on top of this to only allow users to query their own employee data, believing this provides adequate security.
The next day, someone creates a Manager object, a relation from employee to Manager, and Manager to employee.
Now, without having consider security for a second, you've granted all employees the ability to query each other:
query {
employee(userId=uuid) {
name
salary
manager {
employees {
name
salary
}
}
}
}
To say that these problems occur in the wild frequently is an understatement. Since these graphql frameworks also expose introspection capabilities, discovering these exploits can be automated using crawlers. If you write a bug like this, and you will, people will find it.
Please, please stop encouraging people to directly expose their databases through an API.
- rattray 7y agoThe solution to this should be to use the db's security/permissions model. For example, with postgres: https://stackoverflow.com/questions/49261452/combining-row-level-security-with-column-grants https://stackoverflow.com/questions/49261452/combining-row-l...
- holtalanm 7y agoi was thinking this, too. http://postgrest.org/en/v6.0/ http://postgrest.org/en/v6.0/ comes to mind. Its security is based on postgres user permissions.
- paulryanrogers 7y agoSeparate DB user per application user makes connection pooling difficult. And PostgreSQL has more costly connections
- aidos 7y agoHasura avoids this issue by having it’s own row level security model. In fact they go one step further in that for any subscriptions, they can run one query for all connected clients by building a temp table with a row per client using their session variables to join against. (They poll for subscription updates since it’s fast and scales well)
- tflinton 7y agoPgbouncer
- paulryanrogers 7y agoDoes that help with fragmentation though? If the connecting user must be the same then it doesn't add much, except lower connection latency.
- miles-po 7y agoRow-level security is not at all dependent upon DB users/roles. Storing app user data in a table for lookup is sufficient. Storing the user info in a session config value as provided by JWT can be even better. Basic database table replication will suffice for the former. No replication required for the latter.
- hultner 7y agoI've previously seen this handled quite elegantly with SET ROLE/Authorization if I recall correctly, with a rollback at the end.
- dvasdekis 7y agoI agree that this is indeed a problem, but your proposed cure is unnecessarily onerous. At least with Hasura's equivalent product, allowing queries on related objects is an opt-in process for the admin, after each related object is defined. I think a better piece of advice is: Please stop allowing people to query relational models automatically, and surface a separate locked-down schema for the GraphQL user. These GraphQL over DB tools have real value. I've moved from using OpenResty to Hasura in order to surface Postgres APIs, and the time saved has been significant.
- miles-po 7y agoLet's be clear: many folks today are directly exposing their databases through REST. The API protocol really doesn't matter. CRUD has no affinity for any one technology or methodology. GraphQL is no more vulnerable to crawlers than any REST server with OpenAPI on it. And yes, introspection can be disabled. It's not like tools like Hasura, Prisma, or Postgraphile have no security baked into their products, often via a cryptographically signed JWT. Query cost analysis. Query depth limits. Query pattern allow lists. And that's all assuming the GraphQL server has a public IP, which is far from a certainty (just like REST).
- mooted1 7y agoHaving a JWT or whatever authn is irrelevant. The problem is an insufficient authz model. Exposing databases through APIs is not the problem. Exposing relations without authorization is. Under these frameworks, you can add an innocuous relationship between two models that entirely compromises security without even touching the API code. Not only that, but the graph of relations and their associated ACLs is complex. Every time you add a relation, you need to create a graph of your data model and ensure that it's safe. There are far, far more surface area to make a critical error, allowing attackers to exfiltrate large volumes of your data. I've written these bugs in similar modeled systems (there were GQL like systems before GQL). I've fixed these bugs. I've caught these bugs in code review. These bugs are orders of magnitude less likely to happen with a simpler authz model where you don't need to lock down every relation, just the table itself. This is why the GraphQL creators themselves encourage users to put authz at the business layer. 2 out of three examples above literally have no framework authz support. Postgraphile requires setting up row level security policies, meaning you have no control over what layer of code you want authz policies to live; they must be in the database. Even if you are ok with that sacrifice, you still have to find ways to manage this in version control and test, for which there is scarce tooling. Hasura seems to do the right thing here, provided you opt into it. It's not clear if it allows you to easily version control or test your ACLs. > And that's all assuming the GraphQL server has a public IP, which is far from a certainty (just like REST). Security doesn't stop at your VPN. At several hundred engineers, organizations begin implementing internal controls. In fact, my example was an employee comp manager :|.