Is there a way to disable Introspection from the schema?
I don't have an answer for you, but I'm curious what the reason for that would be. I'm only guessing, but I doubt there is a way to do that, since it's not part of the GraphQL spec to be able to disable introspection.
It's not built-in, but you could implement it with a query analyzer. You could check each irep_node.definition.introspection?, and if it returns true, return an AnalysisError in final_value. Good luck, and feel free to follow up here if you have any trouble in the process!
In case anyone else was looking for a simple example, I went ahead and implemented this IntrospectionAnalyzer to block introspection queries in Rails production environments:
class IntrospectionAnalyzer
def initial_value(query)
{
query: query,
is_introspection: false
}
end
def call(memo, visit_type, irep_node)
if irep_node.definition && irep_node.definition.introspection?
memo[:is_introspection] = true
end
memo
end
def final_value(memo)
if memo[:is_introspection] && Rails.env.production?
unauthorized_introspection_query_error
else
memo
end
end
private
def unauthorized_introspection_query_error
GraphQL::AnalysisError.new(
'Introspection queries are not allowed in production.'
)
end
end
Schema:
class ApiSchema < GraphQL::Schema
use GraphQL::Tracing::NewRelicTracing
use GraphQL::Batch
max_depth 8
mutation Types::Mutation
query Types::Query
query_analyzer IntrospectionAnalyzer.new
end
Hope this helps, any improvements are welcome.
Security tips 馃憤
class QueryAnalyzer < GraphQL::Analysis::AST::Analyzer
QUERY_ALLOWED_DEPTH = 5
def initialize(query)
super
@max_depth = 0
@current_depth = 0
end
def on_enter_field(node, parent, visitor)
@current_depth += 1
end
def on_leave_field(node, parent, visitor)
# NOTE: https://graphql-ruby.org/schema/introspection
introspection_field_names = %w[__schema __type]
field_def = visitor.field_definition
if field_def.introspection? && introspection_field_names.include?(field_def.graphql_name)
@introspection_present = true
end
# TODO: Check depth per field
if @max_depth < @current_depth
@max_depth = @current_depth
end
@current_depth -= 1
end
def result
if @introspection_present
GraphQL::AnalysisError.new("Not authorized to query schema internals") unless authorized?
elsif depth_exceeded?
GraphQL::AnalysisError.new("Query depth #{@max_depth} exceeds allowed #{query_allowed_depth}")
end
end
private def authorized?
Rails.env.development? ||
ENV["GRAPHQL_SCHEMA_PASSWORD"].present? &&
ENV["GRAPHQL_SCHEMA_PASSWORD"] == query.context[:request].headers["Schema-Token"]
end
private def depth_exceeded?
@max_depth > query_allowed_depth
end
private def query_allowed_depth
QUERY_ALLOWED_DEPTH
end
end
class ApiSchema < GraphQL::Schema
# NOTE: Do not enable `max_depth`, use custom analyser
# max_depth 8
mutation(Types::MutationType)
query(Types::QueryType)
use(GraphQL::Analysis::AST)
query_analyzer(QueryAnalyzer)
end
apollo service:download --endpoint=https://my-api-endpoint/graphql --header="Schema-Token: XXX"
query {
__schema {
queryType {
fields {
name
type {
kind
ofType {
kind
name
}
}
}
}
}
}
Most helpful comment
In case anyone else was looking for a simple example, I went ahead and implemented this IntrospectionAnalyzer to block introspection queries in Rails production environments:
Schema:
Hope this helps, any improvements are welcome.