Skip to content

Commit 2ccaca9

Browse files
committed
structure + some text, missing examples
1 parent c35374f commit 2ccaca9

File tree

1 file changed

+212
-5
lines changed

1 file changed

+212
-5
lines changed

modules/ROOT/pages/security/securing-a-graphql-api.adoc

Lines changed: 212 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,228 @@
44

55
Lorem ipsum.
66

7+
78
== Prerequisites
89

10+
Set up a new AuraDB instance.
11+
Refer to link:https://neo4j.com/docs/aura/getting-started/create-instance/[Creating a Neo4j Aura instance].
12+
13+
14+
== Security-related directives
15+
16+
Lorem ipsum.
17+
18+
19+
=== Authentication
20+
921
Lorem ipsum.
22+
Explanation on different auth options.
23+
24+
25+
==== JWT
26+
27+
Lorem ipsum.
28+
29+
30+
==== Lorem?
31+
32+
ipsum.
1033

11-
== Directives
1234

1335
=== Authorization
1436

1537
Validate versus filter.
1638
We want to ensure we don't report back database internals to end users. Validate throws an error, which could be a hint of what exists or not.
1739

18-
== Further reading
1940

20-
Explanation on different auth options
41+
== Best practice checklist
42+
43+
Besides authentication and authorization considerations, there are a couple of worthwhile best practices to increase your API's security.
44+
45+
46+
=== Avoid introspection and data field suggestions
47+
48+
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.
49+
50+
Both potentially expose information that can be used to gain insight on specifics of your GraphQL schema and execute targeted malicious opLorem ipsumerations.
51+
Be sure to deactivate both in a customer-facing real-life scenario.
52+
53+
54+
=== Catch internal errors
55+
56+
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.
57+
58+
For example, the following error reveals information XY:
59+
60+
[source, json, indent=0]
61+
----
62+
"data": {
63+
"field": "value"
64+
}
65+
----
66+
67+
You can use Apollo Server's link:https://www.apollographql.com/docs/apollo-server/data/errors[Error Handling] to catch such internal errors and then decide how to pass this on to your users:
68+
69+
[source, typescript, indent=0]
70+
----
71+
import { ApolloServerErrorCode } from '@apollo/server/errors';
72+
73+
if (error.extensions?.code === ApolloServerErrorCode.GRAPHQL_PARSE_FAILED) {
74+
// respond to the syntax error
75+
76+
} else if (error.extensions?.code === "MY_CUSTOM_CODE") {
77+
// do something else
78+
79+
}
80+
----
81+
82+
83+
=== Limit query depth
84+
85+
Limiting query depth disallows potentially harmful queries such as the following recursive query:
86+
87+
[source, graphql, indent=0]
88+
----
89+
query {
90+
order(id: 42) {
91+
products {
92+
order {
93+
products {
94+
order {
95+
products {
96+
order {
97+
# and so on...
98+
}
99+
}
100+
}
101+
}
102+
}
103+
}
104+
}
105+
}
106+
----
107+
108+
This can be achieved with a package such as link:https://www.npmjs.com/package/graphql-depth-limit[graphql-depth-limit]:
109+
110+
[source, typescript, indent=0]
111+
----
112+
import depthLimit from 'graphql-depth-limit'
113+
import express from 'express'
114+
import graphqlHTTP from 'express-graphql'
115+
import schema from './schema'
116+
117+
const app = express()
118+
119+
app.use('/graphql', graphqlHTTP((req, res) => ({
120+
schema,
121+
validationRules: [ depthLimit(10) ]
122+
})))
123+
----
21124

22-
Security best practices
23125

24-
link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/[Role-based access control]
126+
=== Paginate list fields
127+
128+
Returning large query result lists can negatively affect server performance.
129+
For example, a query like the following would return a siginificant number of nodes:
130+
131+
[source, graphql, indent=0]
132+
----
133+
query {
134+
order(first: 1000) {
135+
orderID
136+
products(last: 100) {
137+
productName
138+
productCategory
139+
}
140+
}
141+
}
142+
----
143+
144+
To avoid this, you can cap the input number directly in your resolvers, for example like this:
145+
146+
[source, graphql, indent=0]
147+
----
148+
// example
149+
----
150+
151+
Alternatively, use a library such as link:https://github.com/joonhocho/graphql-input-number[graphql-input-number]:
152+
153+
[source, typescript, indent=0]
154+
----
155+
import {
156+
GraphQLInputInt,
157+
GraphQLInputFloat,
158+
} from 'graphql-input-number';
159+
160+
const argType = GraphQLInputInt({
161+
name: 'OneToNineInt',
162+
min: 1,
163+
max: 9,
164+
});
165+
166+
new GraphQLObjectType({
167+
name: 'Query',
168+
fields: {
169+
input: {
170+
type: GraphQLInt,
171+
args: {
172+
number: {
173+
type: argType,
174+
},
175+
},
176+
resolve: (_, {number}) => {
177+
178+
// 'number' IS AN INT BETWEEN 1 to 9.
179+
180+
};
181+
},
182+
},
183+
});
184+
----
185+
186+
187+
=== Rate-limit your API
188+
189+
Rate-limiting an API means setting an upper bound to how many requests a client can send in a certain amount of time or how costly those requests may be.
190+
There is more than one approach.
191+
Several are outlined in the following sections.
192+
193+
194+
==== Rate limit scores
195+
196+
Refer to GitHub's link:https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#primary-rate-limit-for-authenticated-users[Rate limits for the REST API].
197+
198+
199+
==== Query cost points
200+
201+
The link:https://shopify.dev/docs/api/usage/limits#the-leaky-bucket-algorithm[leaky bucket algorithm].
202+
203+
==== Query cost analysis
204+
205+
link:https://github.com/pa-bru/graphql-cost-analysis[raphql-cost-analysis]
206+
207+
208+
=== Use timeouts
209+
210+
To prevent the API from not responding or falling victim to denial of service attacks, it is feasible to make use of timeouts.
211+
This way, subsequent queries will not be blocked by a long-running previous query.
212+
213+
There are many ways and places to use timeouts.
214+
Here are a few examples.
215+
216+
// examples
217+
218+
219+
=== Validate user input
220+
221+
User input may potentially be malicious, for example, it could contain code snippets which get executed when running queries against the database.
222+
223+
Follow the input validation methods summarized in the link:https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html#input-validation[OWASP Cheat Sheet Series].
224+
225+
// Examples?
226+
227+
228+
229+
== Further reading
230+
231+
Neo4j has a link:https://neo4j.com/docs/operations-manual/current/authentication-authorization/manage-privileges/[Role-based access control] mechanism that can be leveraged to increase security even further.

0 commit comments

Comments
 (0)