GraphQL security
You must address the specific security concerns of the GraphQL APIs: It is possible to construct very expensive queries for the GraphQL server to execute and therefore cause a denial of service attack. You can mitigate this issue in two ways:
-
Use query complexity analysis to reject queries that ask for too much data.
-
Use query depth analysis to reject deeply nested queries.
These mitigations are good for any public API that arbitrary queries are run against. For APIs that are supporting only queries that you control, whitelisting mitigates the issue completely.
The default settings of complexity are specified in /App_Config/Sitecore/Services.GraphQL/Sitecore.Services.GraphQL.config
.
Caching and whitelisting
The GraphQL API uses a Sitecore cache instance to store incoming queries that have been pre-parsed and validated. After it has been validated, a query no longer needs to be validated again and is retrieved from cache. The default cache size is 10 MB, and you can set the size for each endpoint.
The default caching mechanism also enables support for the Automatic Persisted Queries (APQ) protocol developed by Apollo. APQ clients send the hash of a query they want to execute; if the query is cached, it is executed using its hash instead of sending the complete query contents. If the query is not in the cache, a specific error reply is sent, and the client re-issues the request with the query text so that the server can cache it for future reuse. This technique can significantly reduce incoming bandwidth with minimal effort.
The cache mechanism also gives you a way to use a query whitelist. Whitelisting can provide both protection against API denial-of-service attacks, as well as reduced bandwidth usage (exactly like APQ) because an entire query need not be sent, only a pointer/hash to the query. The GraphQL API supports the concept of an authoritative cache, where the only allowed queries are those in the cache. The WhitelistingGraphQLQueryCache
class provides a default whitelist implementation that has the following features:
-
Stores allowed queries as files in a folder on disk (easy to review for source control commits).
-
Allows you to copy these files into the whitelist if you use
.graphql
files to store queries in your app and you store one GQL operation per file. -
Supports a learning mode where queries received are added to whitelist files. This is good if you have end-to-end tests that run every query in the application at build time; learning mode is disabled in production.
-
Uses APQ-compatible hashes for stored queries, so an APQ client can connect and get all the APQ functionality without the automatic part (executed queries must be on the whitelist).
-
Reloads whitelists when they are changed.
The cache configuration is registered on the endpoint in the config file. Custom cache implementations must inherit from IGraphQLQueryCacheFactory
. The following example shows you how to register the whitelist cache:
<cache type="Sitecore.Services.GraphQL.Hosting.QueryTransformation.Caching.WhitelistingGraphQLQueryCache, Sitecore.Services.GraphQL.NetFxHost">
<!-- path can be virtual (/... or ~/...) or physical (c:\...) -->
<param desc="path">~/App_Data/GraphQL/myendpoint-whitelist</param>
<param desc="hashFactory">
<hashFactory type="Sitecore.Services.GraphQL.Hosting.Hashing.Sha256DocumentHashFactory, Sitecore.Services.GraphQL.NetFxHost"/>
</param>
<!-- learning allows the whitelist to add incoming queries; then disable this in production -->
<learningEnabled>true</learningEnabled>
</cache>
Authorization
You perform GraphQL authorization at the business logic/schema level. Each resolver function must check for authorization as needed for a particular graph node.
If the API as a whole needs custom authorization, you can register an IAuthorizationFilter
-based class to authorize each API request under the <security>
section:
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
<sitecore>
<api>
<GraphQL>
<endpoints>
<master>
<security>
<authorization type="My.Type.Derived.From.IAuthorizationFilter, My.Assembly" />
</security>
</master>
</endpoints>
</GraphQL>
</api>
</sitecore>
</configuration>