-
Notifications
You must be signed in to change notification settings - Fork 15
Securing a Graphql API #280
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 7.x
Are you sure you want to change the base?
Conversation
|
This PR includes documentation updates New pages: |
keremgocen
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's looking good so far and has valuable information. However there are a few typos and I think we can make it more practical with some code examples. I left a few comments.
|
|
||
| For the same reason it is advisable to avoid introspection and data field suggestions, it can make your API more secure to catch internal errors and redact which information you want to pass on to the end user. | ||
|
|
||
| For example, the following error reveals information XY: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this also a placeholder XY? feels like the JSON example here could be more illustrative?
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
| // example |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing example here I think?
| There are many ways and places to use timeouts. | ||
| Here are a few examples. | ||
|
|
||
| // examples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing example?
|
|
||
| Follow the input validation methods summarized in the link:https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html#input-validation[OWASP Cheat Sheet Series]. | ||
|
|
||
| // Examples? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also here, do we want an actual example?
| ---- | ||
| import depthLimit from 'graphql-depth-limit' | ||
| import express from 'express' | ||
| import graphqlHTTP from 'express-graphql' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we maybe use a more up to date example here? we could replace the deprecated express-graphql with graphql-http, apparently. (cc: @mjfwebb)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll replace this example with an example using GraphQL Armor
Co-authored-by: kerem <[email protected]>
| Returning large query result lists can negatively affect server performance. | ||
| For example, a query like the following would return a siginificant number of nodes: | ||
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
| query { | ||
| order(first: 1000) { | ||
| orderID | ||
| products(last: 100) { | ||
| productName | ||
| productCategory | ||
| } | ||
| } | ||
| } | ||
| ---- | ||
|
|
||
| To avoid this, you can cap the input number directly in your resolvers, for example like this: | ||
|
|
||
| [source, graphql, indent=0] | ||
| ---- | ||
| // example | ||
| ---- | ||
|
|
||
| Alternatively, use a library such as link:https://github.com/joonhocho/graphql-input-number[graphql-input-number]: | ||
|
|
||
| [source, typescript, indent=0] | ||
| ---- | ||
| import { | ||
| GraphQLInputInt, | ||
| GraphQLInputFloat, | ||
| } from 'graphql-input-number'; | ||
| const argType = GraphQLInputInt({ | ||
| name: 'OneToNineInt', | ||
| min: 1, | ||
| max: 9, | ||
| }); | ||
| new GraphQLObjectType({ | ||
| name: 'Query', | ||
| fields: { | ||
| input: { | ||
| type: GraphQLInt, | ||
| args: { | ||
| number: { | ||
| type: argType, | ||
| }, | ||
| }, | ||
| resolve: (_, {number}) => { | ||
| // 'number' IS AN INT BETWEEN 1 to 9. | ||
| }; | ||
| }, | ||
| }, | ||
| }); | ||
| ---- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
replace with type definition solution (server side)
mention: denial of service prevention
| ==== Query cost points | ||
|
|
||
| The link:https://shopify.dev/docs/api/usage/limits#the-leaky-bucket-algorithm[leaky bucket algorithm]. | ||
|
|
||
| ==== Query cost analysis | ||
|
|
||
| link:https://github.com/pa-bru/graphql-cost-analysis[graphql-cost-analysis] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
link to graphql armor
| To prevent the API from not responding or falling victim to denial of service attacks, it is feasible to make use of timeouts. | ||
| This way, subsequent queries will not be blocked by a long-running previous query. | ||
|
|
||
| There are many ways and places to use timeouts. | ||
| Here are a few examples. | ||
|
|
||
| // examples |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
refer to transaction config in context
| . Set up a new AuraDB instance. | ||
| Refer to link:https://neo4j.com/docs/aura/getting-started/create-instance/[Creating a Neo4j Aura instance]. | ||
| . Populate the instance with the Northwind data set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we sure we want to make this a pre-requisite? This can also be done with an on-prem db
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's the same in graphql-modeling.adoc, following the "aura first" approach (making aura the default UX journey)
|
|
||
| === Authentication | ||
|
|
||
| The xref:security/authentication.adoc[`@authentication` directive] can be applied globally, only to certain fields or only to certain types, and only for certain operations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can be applied globally but only to certain elements?
Is there a typo? I'm not sure I understand this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rephrased
it's either ... or ... or (added "either" and made it active voice)
|
|
||
| The xref:security/authentication.adoc[`@authentication` directive] can be applied globally, only to certain fields or only to certain types, and only for certain operations. | ||
|
|
||
| Add admin authorization for operations on customers, orders, products, categories and suppliers: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use different directives to differentiate between authentication (we can identify who you are) and authorization (you have the correct rights to access the data)
While this particular example is a bit on the edge, I would avoid using the word "authorization" when trying to add examples of authentication to avoid confusion
In this case, we are validating your tokens are valid, including the role
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rephrased a bit
modules/ROOT/content-nav.adoc
Outdated
| * *Reference* | ||
| * xref:security/index.adoc[] | ||
| ** xref:security/securing-a-graphql-api.adoc[] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is reference the right place for this? this feels more like a tutorial
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you're right
security is in reference, but we have this nearly empty how-to section (we're going to tackle that next)
let's put it there for now 👍
| ---- | ||
| type Customer | ||
| @node | ||
| @authentication( <1> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These points in the code are referenced with numbers, but I'm not sure what the reference to these are. Is this just to mark the code changes form the previous version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think i was going to do something with them, but then did not.
removed :)
| For instance, if the server side is trying to parse the `roles` field that was introduced in xref:#_authentication[], then the JWT should contain that. | ||
| With `@jwtClaim`, you can specify a path to a customer ID in a nested location. | ||
| For example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jwtClaim is explained, but the directive @jwt is not mentioned anywhere, maybe worth mentioning that this directive allows to define the types of the jwt data?
| While the xref:getting-started/graphql-aura.adoc[Getting started page for GraphQL and Aura Console] advocates to both **Enable introspection** as well as **Enable field suggestions**, this is not recommended when considering security. | ||
|
|
||
| Both potentially expose information that can be used to gain insight on specifics of your GraphQL schema and execute targeted malicious operations. | ||
| Be sure to deactivate both in a customer-facing real-life scenario. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is definitely recommended in production systems. Introspection and field suggestions are not necessarily insecure, and some applications, such as public APIs may want these features, may be worth rewording this to make sure these are recommendations, but not critical steps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rephrased a bit
|
|
||
| You can prevent denial of service attacks based on queries such as this by paginating query results. | ||
|
|
||
| A server-side pagination solution based on type definitions could look like this: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mmm, it feels like this example is missing the @limit directive that enforces limit
Or this is covering a different point?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where would you place it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, I'm not sure what this example is covering, but if it is indeed server-side enforced pagination (https://neo4j.com/docs/graphql/current/directives/custom-logic/#_limit)
I'd say that we can add a @limit to any of the nodes of the initial typeDefinitions?
| You can set a timeout via the GraphQL Library driver, see xref:driver-configuration.adoc#_transaction_configuration_in_context[Transaction configuration in context]. | ||
|
|
||
|
|
||
| == Further reading |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend reading the GraphQL security section: https://www.graphql.org/learn/security/
This covers up to date best practices for GraphQL in general
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added
this PR is kinda dependent on #274 now
would be good to publish them simultaneously
274 has two js examples missing