| 
4 | 4 | 
 
  | 
5 | 5 | Lorem ipsum.  | 
6 | 6 | 
 
  | 
 | 7 | + | 
7 | 8 | == Prerequisites  | 
8 | 9 | 
 
  | 
 | 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 | + | 
9 | 21 | Lorem ipsum.  | 
 | 22 | +Explanation on different auth options.  | 
 | 23 | + | 
 | 24 | + | 
 | 25 | +==== JWT  | 
 | 26 | + | 
 | 27 | +Lorem ipsum.  | 
 | 28 | + | 
 | 29 | + | 
 | 30 | +==== Lorem?  | 
 | 31 | + | 
 | 32 | +ipsum.  | 
10 | 33 | 
 
  | 
11 |  | -== Directives  | 
12 | 34 | 
 
  | 
13 | 35 | === Authorization  | 
14 | 36 | 
 
  | 
15 | 37 | Validate versus filter.  | 
16 | 38 | 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.  | 
17 | 39 | 
 
  | 
18 |  | -== Further reading  | 
19 | 40 | 
 
  | 
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 | +----  | 
21 | 124 | 
 
  | 
22 |  | -Security best practices  | 
23 | 125 | 
 
  | 
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