Allow only specific queries
Prevent malicious requests to your GraphQL servers by specifying allowed queries.For example, you might know that clients send only a limited set of GraphQL queries to your servers. To prevent your GraphQL servers from resolving potentially malicious requests, you specify that list of expected queries so that the servers immediately return an error for queries that are not in that list.
Before you begin
- Set up Gloo Gateway in a single cluster.
- Deploy sample apps.
- Configure an HTTP listener on your gateway. The
RouteTable
in this guide is not required, because you create a GraphQL-specific route table instead. - Follow the Get started guide to define example GraphQL schema and resolvers and configure routing.
Configure policies to allow specific GraphQL queries
You can apply a GraphQLAllowedQueryPolicy
policy at the route level. For more information, see Applying policies.
Review the following sample configuration files.
apiVersion: security.policy.gloo.solo.io/v2
kind: GraphQLAllowedQueryPolicy
metadata:
name: bookinfo-allowed-queries
namespace: bookinfo
spec:
applyToRoutes:
- route:
labels:
route: graphql-bookinfo
config:
allowedQueryHashes:
- <query_hash>
Review the following table to understand this configuration. For more information, see the API docs.
Setting | Description |
---|---|
spec.applyToRoutes |
Use labels to configure which GraphQL routes to apply the policy to. This example label matches the app and route from the example route table that you previously applied in the GraphQL getting started guide. If omitted or empty, the policy applies to no routes in the workspace. If more than one GraphQLAllowedQueryPolicy applies to a GraphQL route, the oldest policy is applied. |
allowedQueryHashes |
A list of SHA-256 hashed GraphQL queries that you allow the GraphQL server to resolve. Queries that are not sent as hashes are hashed and compared against the list. If a query hash is not in this list, the server returns an error. If ommitted or empty, all queries are allowed. |
Verify a GraphQLAllowedQueryPolicy
-
Save a simple GraphQL query string in an environment variable. For example, let’s say you want to allow only queries that return the title, reviews, and ratings from the Bookinfo app.
export QUERY="query MyProductsForHome { productsForHome { title reviews { reviewer, text }, ratings { reviewer, numStars } } }"
-
Compute the SHA-256 hash for this query by using the
shasum
command, and save the hash in an environment variable.shasum
prints both the hash and the name of the input file, but because the SHA-256 hash is always 64 characters long, thehead
command takes only the first 64 characters of the output.export QUERY_SHA256=$(echo -n $QUERY | shasum -a 256 | head -c 64) echo $QUERY_SHA256
Example hash:
01f818a19df3fbe19940b7af2e0fa5adcf1884686bb4d3e289623d5fa1875231
-
Apply the following example policy in your cluster. This policy creates an allowlist that includes only the hash for the specified query, and applies to the
graphql-bookinfo
route that you created in the Get started guide.kubectl apply -f - << EOF apiVersion: security.policy.gloo.solo.io/v2 kind: GraphQLAllowedQueryPolicy metadata: name: bookinfo-allowed-queries namespace: bookinfo spec: applyToRoutes: - route: labels: route: graphql-bookinfo config: allowedQueryHashes: - ${QUERY_SHA256} EOF
-
To send a cURL request with the allowed query to the GraphQL services, encode the query in JSON format. Note that GraphQL clients typically convert the request automatically.
export QUERY_JSON={\"query\":\"$QUERY\"}
-
Send the allowed GraphQL query to the route through the ingress gateway, and format the output with
jq
.curl --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/graphql \ -d $QUERY_JSON -H "Content-Type: application/json" | jq
curl --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/graphql \ -d $QUERY_JSON -H "Content-Type: application/json" | jq
{ "data": { "productsForHome": [ { "title": "The Comedy of Errors", "reviews": [ { "reviewer": "Reviewer1", "text": "An extremely entertaining play by Shakespeare. The slapstick humour is refreshing!" }, { "reviewer": "Reviewer2", "text": "Absolutely fun and entertaining. The play lacks thematic depth when compared to other plays by Shakespeare." } ], "ratings": [ { "reviewer": "Reviewer1", "numStars": 5 }, { "reviewer": "Reviewer2", "numStars": 4 } ] } ] } }
-
Now, save a different GraphQL query that is not listed in the policy in an environment variable.
export BAD_QUERY="query MyProductsForHome { productsForHome { title } }" export BAD_QUERY_JSON={\"query\":\"$BAD_QUERY\"}
-
Send the query to the same route.
curl --resolve www.example.com:80:${INGRESS_GW_IP} http://www.example.com:80/graphql \ -d $BAD_QUERY_JSON -H "Content-Type: application/json" | jq
curl --resolve www.example.com:443:${INGRESS_GW_IP} https://www.example.com:443/graphql \ -d $BAD_QUERY_JSON -H "Content-Type: application/json" | jq
{ "errors": [ { "message": "hash ba7faf706579b441e281376ba5a87d5047e79eb52dcf6ac0eb34eb85ed53b053 not found in allowlist for query: 'query MyProductsForHome { productsForHome { title } }'" } ] }
-
Optional: Clean up the resource that you created.
kubectl -n bookinfo delete GraphQLAllowedQueryPolicy bookinfo-allowed-queries