@@ -189,77 +189,41 @@ app.use(postgraphile(pool, "public", { ... }));
189189
190190#### Simple: Query Allowlist ("persisted queries" / "persisted operations")
191191
192- If you do not intend to open your API up to third parties to run arbitrary
193- queries against then using persisted queries as a query whitelist to protect
194- your GraphQL endpoint is a great and highly recommended solution. This technique
195- ensures that only the queries you use in your own applications can be executed
196- on the server (but you can of course change the variables).
197-
198- This technique has a few caveats:
199-
200- - Your API will only accept queries that you've approved, so it's not suitable
201- if you want third parties to run arbitrary queries
202- - You must be able to generate a unique ID from each query; e.g. a hash
203- - You must use "static GraphQL queries" - that is the queries must be known at
204- build time of your application/webpage, and only the variables fed to those
205- queries can change at run-time
206- - You must have a way of sharing these queries between the application and the
207- server
208- - You must be careful not to use variables in dangerous places; for example
209- don't write ` allUsers(first: $myVar) ` as a malicious attacker could set
210- ` $myVar ` to 2147483647 in order to cause your server to process as much data
211- as possible.
212-
213- _ ** THIS SECTION IS OUT OF DATE** ; please refer to the new
214- [ @graphile/persisted-operations project] ( https://github.com/graphile/persisted-operations )
215- for up to date information and implementation details._
216-
217- ~~ PostGraphile currently doesn't have this functionality built in, but it's
218- fairly easy to add it when using PostGraphile as an express middleware, a simple
219- implementation might look like this:~~
220-
221- ``` js{9-18}
222- const postgraphile = require("postgraphile");
223- const express = require("express");
224- const bodyParser = require("body-parser");
225-
226- const app = express();
227- app.use(bodyParser.json());
228-
229- /**** BEGINNING OF CUSTOMIZATION ****/
230- const persistedQueries = require("./persistedQueries.json");
231-
232- app.use("/graphql", async (req, res, next) => {
233- // TODO: validate req.body is of the right form
234- req.body.query = {}.hasOwnProperty.call(persistedQueries, req.body.id)
235- ? persistedQueries[req.body.id]
236- : null;
237- next();
238- });
239- /**** END OF CUSTOMIZATION *** */
240-
241- app.use(postgraphile());
242-
243- app.listen(5000);
244- ```
245-
246- ~~ i.e. a simple middleware mounted before postgraphile that manipulates the
247- request body.~~
248-
249- ~~ I (Benjie) personally use my forks of Apollo's ` persistgraphql ` tools to help
250- me manage the persisted queries themselves:~~
251-
252- - ~~ https://github.com/benjie/persistgraphql ~~
253- - ~~ https://github.com/benjie/persistgraphql-webpack-plugin ~~
254-
255- ~~ These forks generate hashes rather than numbers; which make the persisted
256- queries consistent across multiple builds and applications (website, mobile,
257- browser plugin, ...).~~
258-
259- ** NOTE** : even if you're using persisted operations, it can still be wise to
260- implement advanced protections as it enables you to catch unnecessarily
261- expensive queries during development, before you start facing performance
262- bottlenecks down the line.
192+ If you do not intend to allow third parties to run arbitrary operations against
193+ your API then using
194+ [ persisted operations] ( https://github.com/graphile/persisted-operations ) as a
195+ query allowlist is a highly recommended solution to protect your GraphQL
196+ endpoint. This technique ensures that only the operations you use in your own
197+ applications can be executed on the server, preventing malicious (or merely
198+ curious) actors from executing operations which may be more expensive than those
199+ you have written.
200+
201+ This technique is suitable for the vast majority of use cases and supports many
202+ GraphQL clients, but it does have a few caveats:
203+
204+ - Your API will only accept operations that you've approved, so it's not
205+ suitable if you want third parties to run arbitrary custom operations.
206+ - You must be able to generate a unique ID (e.g. a hash) from each operation at
207+ build time of your application/web page - your GraphQL operations must be
208+ "static". It's important to note this only applies to the operation document
209+ itself, the variables can of course change at runtime.
210+ - You must have a way of sharing these static operations from the application
211+ build process to the server so that the server will know what operation the ID
212+ represents.
213+ - You must be careful not to use variables in dangerous places within your
214+ operation; for example if you were to use ` allUsers(first: $myVar) ` a
215+ malicious attacker could set ` $myVar ` to 2147483647 to cause your server to
216+ process as much data as possible. Use fixed limits, conditions and orders
217+ where possible, even if it means having additional static operations.
218+ - It does not protect you from writing expensive queries yourself; it may be
219+ wise to combine this technique with a cost estimation technique such as that
220+ provided by the [ Graphile Pro plugin] ( /postgraphile/pricing/ ) to help guide
221+ your developers and avoid accidentally writing expensive queries.
222+
223+ PostGraphile has first-party support for persisted operations via the open
224+ source
225+ [ @graphile/persisted-operations ] ( https://github.com/graphile/persisted-operations )
226+ plugin; we recommend its use to the vast majority of our users.
263227
264228#### Advanced
265229
0 commit comments