From c432022582e60634da6347743258d82106ebbf26 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Fri, 24 Feb 2023 18:53:12 -0800 Subject: [PATCH 1/7] docs: start secu Signed-off-by: QuentinN42 --- docs/index.rst | 1 + docs/security/index.rst | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 docs/security/index.rst diff --git a/docs/index.rst b/docs/index.rst index 05b7fd87..d0297d85 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,6 +10,7 @@ Contents: types/index execution/index relay/index + security/index testing/index api/index diff --git a/docs/security/index.rst b/docs/security/index.rst new file mode 100644 index 00000000..744b39ab --- /dev/null +++ b/docs/security/index.rst @@ -0,0 +1,10 @@ +====================== +Security consideration +====================== + +It is crucial to follow security best practices while deploying a GraphQL application to production. +GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking appropriate precautions. +Neglecting to do so can expose the API to vulnerabilities like credential leakage or denial of service attacks. + + +In this section, we will discuss the most common security risks and how to mitigate them. From 65af30194cf2a849c72f7ea47122f5f56e3f51aa Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Wed, 15 Mar 2023 15:40:28 -0700 Subject: [PATCH 2/7] refactor: rewording Signed-off-by: QuentinN42 --- docs/security/index.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/security/index.rst b/docs/security/index.rst index 744b39ab..c1d4f035 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -2,9 +2,10 @@ Security consideration ====================== -It is crucial to follow security best practices while deploying a GraphQL application to production. -GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking appropriate precautions. -Neglecting to do so can expose the API to vulnerabilities like credential leakage or denial of service attacks. +As GraphQL is a query language, it allows users to use a wider pannel of inputs than traditional REST APIs. +Due to this feature, GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking +appropriate precautions. Neglecting them can expose the API to vulnerabilities like credential leakage or denial of +service attacks. In this section, we will discuss the most common security risks and how to mitigate them. From 18b7d53428b6d4b139a89e8bd5b610e32187d2e7 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Wed, 15 Mar 2023 19:18:13 -0700 Subject: [PATCH 3/7] refactor: moved queryvalidation.rst Signed-off-by: QuentinN42 --- docs/execution/index.rst | 1 - docs/security/index.rst | 6 +++++- docs/{execution => security}/queryvalidation.rst | 0 3 files changed, 5 insertions(+), 2 deletions(-) rename docs/{execution => security}/queryvalidation.rst (100%) diff --git a/docs/execution/index.rst b/docs/execution/index.rst index f26259d3..dbfbfa72 100644 --- a/docs/execution/index.rst +++ b/docs/execution/index.rst @@ -10,4 +10,3 @@ Execution dataloader fileuploading subscriptions - queryvalidation diff --git a/docs/security/index.rst b/docs/security/index.rst index c1d4f035..6d2e1e38 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -7,5 +7,9 @@ Due to this feature, GraphQL APIs are inherently prone to various security risks appropriate precautions. Neglecting them can expose the API to vulnerabilities like credential leakage or denial of service attacks. - In this section, we will discuss the most common security risks and how to mitigate them. + +.. toctree:: + :maxdepth: 2 + + queryvalidation diff --git a/docs/execution/queryvalidation.rst b/docs/security/queryvalidation.rst similarity index 100% rename from docs/execution/queryvalidation.rst rename to docs/security/queryvalidation.rst From 84c5559ded24fd476cd09ca4023001f8ef59e515 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 16 Mar 2023 11:50:46 -0700 Subject: [PATCH 4/7] feat: max depth Signed-off-by: QuentinN42 --- docs/security/index.rst | 13 +++++- docs/security/maxdepth.rst | 75 +++++++++++++++++++++++++++++++ docs/security/queryvalidation.rst | 41 ----------------- 3 files changed, 86 insertions(+), 43 deletions(-) create mode 100644 docs/security/maxdepth.rst diff --git a/docs/security/index.rst b/docs/security/index.rst index 6d2e1e38..cb7029b4 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -2,14 +2,23 @@ Security consideration ====================== +In this section, we will discuss the most common security risks and how to mitigate them. + As GraphQL is a query language, it allows users to use a wider pannel of inputs than traditional REST APIs. Due to this feature, GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking appropriate precautions. Neglecting them can expose the API to vulnerabilities like credential leakage or denial of service attacks. -In this section, we will discuss the most common security risks and how to mitigate them. +As Graphene is only an engine to run GraphQL queries, it is not responsible for the HTTP layer and this security must be +handled by the web framework you are using. For example, if you are using Django-GraphQL, you may also want to check out +the `Django documentation`_ on how to secure your API. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 + maxdepth queryvalidation + +We have seen the most efficient way to secure your GraphQL API. + +.. _Django documentation: https://docs.djangoproject.com/en/4.1/topics/security/ diff --git a/docs/security/maxdepth.rst b/docs/security/maxdepth.rst new file mode 100644 index 00000000..b3b053cf --- /dev/null +++ b/docs/security/maxdepth.rst @@ -0,0 +1,75 @@ +Depth limit Validator +===================== + +By default, GraphQL queries can be arbitrarily deep. This can lead to a denial of service attack where a client can send +a deeply nested query that will take a long time to execute. For that, you must find a cycle in the graph and iterate +over. + +Example +------- + +For example a simple app that allows you to find your friends and their friends can lead to this query : + +.. code:: graphql + + query { + me { + friends { + friends { + friends { + friends { + ... + # dumping the whole database + } + } + } + } + } + } + +This is not a common use case, your dev team will not do that in the first place. But as your endpoint is publicly +available, you can't be sure that someone will not try to do so. + +Remediation +----------- + +Graphene provides a depth limit validator that can be used to prevent this kind of attack. It can be configured to limit +the depth of all the queries or only some specific ones. The only required argument is ``max_depth`` which is the +maximum allowed depth for any operation in a GraphQL document. The other optional parameters are the following ones : + +- ``ignore``: A list of patterns that, if matched stops recursive depth checking. It can be one of the following : + - ``Callable : (dict[str, int]) -> bool``: A function that receives the current operation and returns a boolean. + - ``Pattern``: A compiled regex pattern that is matched against the operation name. + - ``str``: An operation name. +- ``callback: (dict[str, int]) -> None`` Called each time validation runs. Receives an Object which is a map of the depths for each operation. + +Usage +----- + +Here is an example of how you would implement depth-limiting on your schema. + +.. code:: python + + from graphql import validate, parse + from graphene import ObjectType, Schema, String + from graphene.validation import depth_limit_validator + + + class MyQuery(ObjectType): + name = String(required=True) + + + schema = Schema(query=MyQuery) + + # queries which have a depth more than 20 + # will not be executed. + + validation_errors = validate( + schema=schema.graphql_schema, + document_ast=parse('THE QUERY'), + rules=( + depth_limit_validator( + max_depth=20 + ), + ) + ) diff --git a/docs/security/queryvalidation.rst b/docs/security/queryvalidation.rst index 02e29a35..d0d174c9 100644 --- a/docs/security/queryvalidation.rst +++ b/docs/security/queryvalidation.rst @@ -7,47 +7,6 @@ makes query fail with "Cannot query field on type" error if it doesn't. To help with common use cases, graphene provides a few validation rules out of the box. -Depth limit Validator ---------------------- -The depth limit validator helps to prevent execution of malicious -queries. It takes in the following arguments. - -- ``max_depth`` is the maximum allowed depth for any operation in a GraphQL document. -- ``ignore`` Stops recursive depth checking based on a field name. Either a string or regexp to match the name, or a function that returns a boolean -- ``callback`` Called each time validation runs. Receives an Object which is a map of the depths for each operation. - -Usage ------ - -Here is how you would implement depth-limiting on your schema. - -.. code:: python - - from graphql import validate, parse - from graphene import ObjectType, Schema, String - from graphene.validation import depth_limit_validator - - - class MyQuery(ObjectType): - name = String(required=True) - - - schema = Schema(query=MyQuery) - - # queries which have a depth more than 20 - # will not be executed. - - validation_errors = validate( - schema=schema.graphql_schema, - document_ast=parse('THE QUERY'), - rules=( - depth_limit_validator( - max_depth=20 - ), - ) - ) - - Disable Introspection --------------------- the disable introspection validation rule ensures that your schema cannot be introspected. From f07d6a29c52f78b3b6866992237a6a2470747d9c Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 16 Mar 2023 14:27:31 -0700 Subject: [PATCH 5/7] feat: intropsection and custom validation Signed-off-by: QuentinN42 --- docs/security/customvalidation.rst | 42 +++++++++++++++ docs/security/index.rst | 3 +- docs/security/introspection.rst | 52 +++++++++++++++++++ docs/security/queryvalidation.rst | 82 ------------------------------ 4 files changed, 96 insertions(+), 83 deletions(-) create mode 100644 docs/security/customvalidation.rst create mode 100644 docs/security/introspection.rst delete mode 100644 docs/security/queryvalidation.rst diff --git a/docs/security/customvalidation.rst b/docs/security/customvalidation.rst new file mode 100644 index 00000000..3580df2b --- /dev/null +++ b/docs/security/customvalidation.rst @@ -0,0 +1,42 @@ +Implementing custom validators +============================== + +GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements +standard query validators. For example, there is an validator that tests if queried field exists on queried type, that +makes query fail with "Cannot query field on type" error if it doesn't. + +If you need more complex validation than presented before, you can implement your own query validators. All custom query +validators should extend the `ValidationRule`_ base class importable from the ``graphql.validation.rules`` module. Query +validators are visitor classes. They are instantiated at the time of query validation with one required argument +(context: ASTValidationContext). In order to perform validation, your validator class should define one or more of +``enter_*`` and ``leave_*`` methods. For possible enter/leave items as well as details on function documentation, please +see contents of the visitor module. To make validation fail, you should call validator's report_error method with the +instance of GraphQLError describing failure reason. + +Implementing your custom validators +----------------------------------- + +Here is an example query validator that only allows queries fields with a name of even length. + +.. code:: python + + from graphql import GraphQLError + from graphql.language import FieldNode + from graphql.validation import ValidationRule + + + class MyCustomValidationRule(ValidationRule): + def enter_field(self, node: FieldNode, *_args): + if len(node.name.value) % 2 == 0: + # Here the query length is even, so we allow it. + return + else: + # Here the query length is odd, so we don't want to allow it. + # Calling self.report_error will make the query fail with the error message. + self.report_error( + GraphQLError( + f"Cannot query '{field_name}': length is odd.", node, + ) + ) + +.. _ValidationRule: https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37 \ No newline at end of file diff --git a/docs/security/index.rst b/docs/security/index.rst index cb7029b4..ac2c8735 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -17,7 +17,8 @@ the `Django documentation`_ on how to secure your API. :maxdepth: 1 maxdepth - queryvalidation + introspection + customvalidation We have seen the most efficient way to secure your GraphQL API. diff --git a/docs/security/introspection.rst b/docs/security/introspection.rst new file mode 100644 index 00000000..533d599d --- /dev/null +++ b/docs/security/introspection.rst @@ -0,0 +1,52 @@ +Disable Introspection +===================== + +What is the introspection ? +--------------------------- + +The introspection query is a query that allows you to ask the server what queries and mutations are supported. If you +comes from REST, you can view it as a openapi or swagger schema. + +Disabling it or not ? +--------------------- + +Depending if you are building a private or a public API, you might want to disable introspection or not. If you are +building a public API, the introspection allows consumers (developers) to know what they can do with your API. If you +disable it, it will be harder for them to use your API. But if you are building a private API, the only consumers of +your API will be your own developers. In this case, you might want to keep the introspection open in staging +environments but close it in production to reduce the attack surface. + +Keep in mind that disabling introspection does not prevent hackers to send queries to your API. It just makes it harder +to know what they can do with it. + +Implementation +-------------- + +Graphene provides a validation rule to disable introspection. It ensures that your schema cannot be introspected. You +just need to import the ``DisableIntrospection`` class from ``graphene.validation``. + + +Here is a code example of how you can disable introspection for your schema. + +.. code:: python + + from graphql import validate, parse + from graphene import ObjectType, Schema, String + from graphene.validation import DisableIntrospection + + + class MyQuery(ObjectType): + name = String(required=True) + + + schema = Schema(query=MyQuery) + + # introspection queries will not be executed. + + validation_errors = validate( + schema=schema.graphql_schema, + document_ast=parse('THE QUERY'), + rules=( + DisableIntrospection, + ) + ) diff --git a/docs/security/queryvalidation.rst b/docs/security/queryvalidation.rst deleted file mode 100644 index d0d174c9..00000000 --- a/docs/security/queryvalidation.rst +++ /dev/null @@ -1,82 +0,0 @@ -Query Validation -================ -GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements -standard query validators. For example, there is an validator that tests if queried field exists on queried type, that -makes query fail with "Cannot query field on type" error if it doesn't. - -To help with common use cases, graphene provides a few validation rules out of the box. - - -Disable Introspection ---------------------- -the disable introspection validation rule ensures that your schema cannot be introspected. -This is a useful security measure in production environments. - -Usage ------ - -Here is how you would disable introspection for your schema. - -.. code:: python - - from graphql import validate, parse - from graphene import ObjectType, Schema, String - from graphene.validation import DisableIntrospection - - - class MyQuery(ObjectType): - name = String(required=True) - - - schema = Schema(query=MyQuery) - - # introspection queries will not be executed. - - validation_errors = validate( - schema=schema.graphql_schema, - document_ast=parse('THE QUERY'), - rules=( - DisableIntrospection, - ) - ) - - -Implementing custom validators ------------------------------- -All custom query validators should extend the `ValidationRule `_ -base class importable from the graphql.validation.rules module. Query validators are visitor classes. They are -instantiated at the time of query validation with one required argument (context: ASTValidationContext). In order to -perform validation, your validator class should define one or more of enter_* and leave_* methods. For possible -enter/leave items as well as details on function documentation, please see contents of the visitor module. To make -validation fail, you should call validator's report_error method with the instance of GraphQLError describing failure -reason. Here is an example query validator that visits field definitions in GraphQL query and fails query validation -if any of those fields are blacklisted: - -.. code:: python - - from graphql import GraphQLError - from graphql.language import FieldNode - from graphql.validation import ValidationRule - - - my_blacklist = ( - "disallowed_field", - ) - - - def is_blacklisted_field(field_name: str): - return field_name.lower() in my_blacklist - - - class BlackListRule(ValidationRule): - def enter_field(self, node: FieldNode, *_args): - field_name = node.name.value - if not is_blacklisted_field(field_name): - return - - self.report_error( - GraphQLError( - f"Cannot query '{field_name}': field is blacklisted.", node, - ) - ) - From 672ab9110b187e8b5088ee062cce4a8a896b6556 Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 16 Mar 2023 15:09:49 -0700 Subject: [PATCH 6/7] feat: DAST Signed-off-by: QuentinN42 --- docs/security/dast.rst | 32 ++++++++++++++++++++++++++++++++ docs/security/index.rst | 3 +-- 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 docs/security/dast.rst diff --git a/docs/security/dast.rst b/docs/security/dast.rst new file mode 100644 index 00000000..f045da1a --- /dev/null +++ b/docs/security/dast.rst @@ -0,0 +1,32 @@ +Dynamic Application Security Testing +==================================== + +Continuous security testing +--------------------------- + +One of the best way to stop wondering about security for your API is to be able to scan it each time you deploy it into +staging or production environments. As you run your unit tests in your CI/CD pipeline, you can bullet-proof your GraphQL +application before it even reaches a production environment. + +Security testing tools +---------------------- + +graphql.security +________________ + +`graphql.security`_ is a free, quick graphql security testing tool, allowing you to quickly assess the most common +vulnerabilities in your application. + +Escape +______ + +`Escape`_ is a GraphQL security SaaS platform running an automated pentest tool. + +You can effortlessly incorporate this platform into your current CI/CD pipeline such as Github Actions or Gitlab CIs +which makes it convenient to set up. + +The security notifications will be automatically communicated to your CI/CD platform, enabling you to promptly attend to +them. + +.. _graphql.security: https://graphql.security/ +.. _Escape: https://escape.tech/ diff --git a/docs/security/index.rst b/docs/security/index.rst index ac2c8735..6aae5461 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -19,7 +19,6 @@ the `Django documentation`_ on how to secure your API. maxdepth introspection customvalidation - -We have seen the most efficient way to secure your GraphQL API. + dast .. _Django documentation: https://docs.djangoproject.com/en/4.1/topics/security/ From 1016ba0aab9ce344329e97f8c551bf0006333a3f Mon Sep 17 00:00:00 2001 From: QuentinN42 Date: Thu, 16 Mar 2023 16:55:27 -0700 Subject: [PATCH 7/7] refactor: rewording Signed-off-by: QuentinN42 --- docs/security/customvalidation.rst | 34 +++++++----- docs/security/index.rst | 11 ++-- docs/security/introspection.rst | 23 ++++----- docs/security/maxdepth.rst | 83 +++++++++++++++++++++++------- 4 files changed, 100 insertions(+), 51 deletions(-) diff --git a/docs/security/customvalidation.rst b/docs/security/customvalidation.rst index 3580df2b..375f62f9 100644 --- a/docs/security/customvalidation.rst +++ b/docs/security/customvalidation.rst @@ -1,21 +1,30 @@ Implementing custom validators ============================== -GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements -standard query validators. For example, there is an validator that tests if queried field exists on queried type, that -makes query fail with "Cannot query field on type" error if it doesn't. - -If you need more complex validation than presented before, you can implement your own query validators. All custom query -validators should extend the `ValidationRule`_ base class importable from the ``graphql.validation.rules`` module. Query -validators are visitor classes. They are instantiated at the time of query validation with one required argument -(context: ASTValidationContext). In order to perform validation, your validator class should define one or more of -``enter_*`` and ``leave_*`` methods. For possible enter/leave items as well as details on function documentation, please -see contents of the visitor module. To make validation fail, you should call validator's report_error method with the -instance of GraphQLError describing failure reason. +GraphQL uses query validators to check if Query AST is valid and can be executed. Every GraphQL server implements standard query validators. + +For example, a validator tests if a queried field exists on queried type, making the query fail with a "Cannot query field on type" error if it does not. + +If you need more complex validation than presented before, you can implement your own query validators. Implementing your custom validators ----------------------------------- +All custom query validators should extend the `ValidationRule`_ base class importable from the ``graphql.validation.rules`` module. +Query validators are `Visitor`_ classes. + +Your custom validator should implement some methods of + +In order to perform validation, your validator class should define some one or more of ``enter_*`` or ``leave_*`` methods. + +Foreach methods, you will receive a node object to test: + +- You can now choose to raise an error by calling ``report_error`` method with an instance of GraphQLError describing the failure reason. +- Or you can continue the parsing by returning ``None``. + +Example +------- + Here is an example query validator that only allows queries fields with a name of even length. .. code:: python @@ -39,4 +48,5 @@ Here is an example query validator that only allows queries fields with a name o ) ) -.. _ValidationRule: https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37 \ No newline at end of file +.. _ValidationRule: https://github.com/graphql-python/graphql-core/blob/v3.0.5/src/graphql/validation/rules/__init__.py#L37 +.. _Visitor: https://github.com/graphql-python/graphql-core/blob/d90bf9902ca1639365639d5632861d1e18d672a9/src/graphql/language/visitor.py#L111 diff --git a/docs/security/index.rst b/docs/security/index.rst index 6aae5461..c35d62d0 100644 --- a/docs/security/index.rst +++ b/docs/security/index.rst @@ -2,16 +2,13 @@ Security consideration ====================== -In this section, we will discuss the most common security risks and how to mitigate them. +This section will discuss the most common security risks and how to mitigate them. As GraphQL is a query language, it allows users to use a wider pannel of inputs than traditional REST APIs. -Due to this feature, GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking -appropriate precautions. Neglecting them can expose the API to vulnerabilities like credential leakage or denial of -service attacks. -As Graphene is only an engine to run GraphQL queries, it is not responsible for the HTTP layer and this security must be -handled by the web framework you are using. For example, if you are using Django-GraphQL, you may also want to check out -the `Django documentation`_ on how to secure your API. +Due to this feature, GraphQL APIs are inherently prone to various security risks, but they can be reduced by taking appropriate precautions. Neglecting them can expose the API to vulnerabilities like credential leakage or denial of service attacks. + +As Graphene is only an engine to run GraphQL queries, it is not responsible for the HTTP layer, and this security must be handled by the web framework you are using. For example, if you are using Django-GraphQL, you may also want to check out the `Django documentation`_ on securing your API. .. toctree:: :maxdepth: 1 diff --git a/docs/security/introspection.rst b/docs/security/introspection.rst index 533d599d..0cc8a90b 100644 --- a/docs/security/introspection.rst +++ b/docs/security/introspection.rst @@ -4,27 +4,24 @@ Disable Introspection What is the introspection ? --------------------------- -The introspection query is a query that allows you to ask the server what queries and mutations are supported. If you -comes from REST, you can view it as a openapi or swagger schema. +The introspection query is a query that allows you to ask the server what queries and mutations are supported. If you are a REST user, you can view it as an openapi or swagger schema. -Disabling it or not ? ---------------------- +Should I disable my introspection ? +----------------------------------- -Depending if you are building a private or a public API, you might want to disable introspection or not. If you are -building a public API, the introspection allows consumers (developers) to know what they can do with your API. If you -disable it, it will be harder for them to use your API. But if you are building a private API, the only consumers of -your API will be your own developers. In this case, you might want to keep the introspection open in staging -environments but close it in production to reduce the attack surface. +Whether you are building a private or a public API, you might want to disable introspection : -Keep in mind that disabling introspection does not prevent hackers to send queries to your API. It just makes it harder -to know what they can do with it. +- If you are building a public API, the introspection allows consumers (developers) to know what they can do with your API. If you disable it, it will be harder for them to use your API. +- However, if you are building a private API, the only consumers of your API will be your developers. In this case, keep the introspection open in staging environments but close it in production to reduce the attack surface. + +Remember that disabling introspection does not prevent hackers from sending queries to your API. It just makes it harder to know what they can do with it. Implementation -------------- -Graphene provides a validation rule to disable introspection. It ensures that your schema cannot be introspected. You -just need to import the ``DisableIntrospection`` class from ``graphene.validation``. +Graphene provides a validation rule to disable introspection. It ensures that your schema cannot be introspected. +You just need to import the ``DisableIntrospection`` class from ``graphene.validation``. Here is a code example of how you can disable introspection for your schema. diff --git a/docs/security/maxdepth.rst b/docs/security/maxdepth.rst index b3b053cf..5eae1dad 100644 --- a/docs/security/maxdepth.rst +++ b/docs/security/maxdepth.rst @@ -1,9 +1,9 @@ Depth limit Validator ===================== -By default, GraphQL queries can be arbitrarily deep. This can lead to a denial of service attack where a client can send -a deeply nested query that will take a long time to execute. For that, you must find a cycle in the graph and iterate -over. +By default, GraphQL queries can be arbitrarily deep. But a profound query can lead to a denial of service attack where a client can send a deeply nested query that will take a long time to execute. + +If you find a cycle in the graph, you can iterate over it to generate big queries to put an extreme load on the backend. Example ------- @@ -27,15 +27,21 @@ For example a simple app that allows you to find your friends and their friends } } -This is not a common use case, your dev team will not do that in the first place. But as your endpoint is publicly -available, you can't be sure that someone will not try to do so. +This is not a common use case; your dev team will not do that in the first place. However, as your endpoint is publicly +available, you cannot be sure that someone will not try to do so. Remediation ----------- Graphene provides a depth limit validator that can be used to prevent this kind of attack. It can be configured to limit -the depth of all the queries or only some specific ones. The only required argument is ``max_depth`` which is the -maximum allowed depth for any operation in a GraphQL document. The other optional parameters are the following ones : +the depth of all the queries or only some specific ones. + +Arguments +_________ + +The only required argument is ``max_depth`` which is the maximum allowed depth for any operation in a GraphQL document. + +The other optional parameters are the following ones : - ``ignore``: A list of patterns that, if matched stops recursive depth checking. It can be one of the following : - ``Callable : (dict[str, int]) -> bool``: A function that receives the current operation and returns a boolean. @@ -55,21 +61,60 @@ Here is an example of how you would implement depth-limiting on your schema. from graphene.validation import depth_limit_validator - class MyQuery(ObjectType): - name = String(required=True) + class Me(ObjectType): + id = String() + + schema = Schema(query=Me) + + VALID_QUERY = """ + query valid { + me { + # depth is 1 + id friends { + # depth is 2 + id friends { + # depth is 3 + id + } + } + } + } + """ + + INVALID_QUERY = """ + query evil { + me { + # depth is 1 + id friends { + # depth is 2 + id friends { + # depth is 3 + id friends { + # depth is 4 + id + } + } + } + } + } + """ + RULES = (depth_limit_validator(max_depth=3), ) - schema = Schema(query=MyQuery) + VALID_RESPONSE = validate( + schema=schema.graphql_schema, + document_ast=parse(VALID_QUERY), + rules=RULES + ) - # queries which have a depth more than 20 - # will not be executed. + assert len(VALID_RESPONSE) == 0 - validation_errors = validate( + INVALID_RESPONSE = validate( schema=schema.graphql_schema, - document_ast=parse('THE QUERY'), - rules=( - depth_limit_validator( - max_depth=20 - ), - ) + document_ast=parse(INVALID_QUERY), + rules=RULES ) + + + assert len(INVALID_RESPONSE) == 1 + assert INVALID_RESPONSE[0].message == "'evil' exceeds maximum operation depth of 3."