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

This guide assumes that you use the same names for components like clusters, workspaces, and namespaces as in the getting started, and that your Kubernetes context is set to the cluster you store your Gloo config in (typically the management cluster). If you have different names, make sure to update the sample configuration files in this guide.
  1. Set up Gloo Gateway in a single cluster.
  2. Deploy sample apps.
  3. Configure an HTTP listener on your gateway. The RouteTable in this guide is not required, because you create a GraphQL-specific route table instead.
  4. 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

  1. 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 } } }"
    
  2. 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, the head 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
    
  3. 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
    
  4. 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\"}
    
  5. 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
    
    You receive the expected response, because the query was allowed:

    {
      "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
              }
            ]
          }
        ]
      }
    }
    
  6. 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\"}
    
  7. 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
    
    This time, you receive an error from the GraphQL server because the query is not in the route's allowlist:

    {
      "errors": [
        {
          "message": "hash ba7faf706579b441e281376ba5a87d5047e79eb52dcf6ac0eb34eb85ed53b053 not found in allowlist for query: 'query MyProductsForHome { productsForHome { title } }'"
        }
      ]
    }
    
  8. Optional: Clean up the resource that you created.

    kubectl -n bookinfo delete GraphQLAllowedQueryPolicy bookinfo-allowed-queries