Problem Statement
Introspection aids a potential attacker in enumerating key pieces of information about the overall GraphQL system. For example, an attacker could get all the query names, variable names, etc. that makes it easier to construct and unexpectedly call query, mutation, or subscription calls. Example of such queries
Getting the list of the types:
In the above result, all those preceding with double underscore, indicates that they are part of the introspection system.
Getting the fields for the specific schema:
Solution
The approach here is the same as in introspection – disable GraphQL schema exploration in public ally accessible environments unless necessary.
There is no OOTB mechanism to disable introspection query or _schema query to GraphQL endpoints.
There are two alternatives for solving this problem.
Option 1: The first approach to avoid the potential attacker to enter the system would be to fully rely on Persisted Queries and disable the GraphQL endpoints at the dispatcher level.
/0106 { /type "deny" /method '(POST|OPTIONS)' /url "/content/_cq_graphql/test/endpoint.json" }
or
/0106 { /type "deny" /method '(POST|OPTIONS)' /url "/content/_cq_graphql/*/endpoint.json" }
This way either your specific endpoint or all the endpoints could be blocked.
Option 2: The queries could be disabled by introduction custom javax.servlet.Filter
(service = javax.servlet.Filter.class, property = {
"sling.filter.pattern" + "=/content/cq:graphql/.*/endpoint",
"sling.filter.methods" + "=POST",
"sling.filter.extensions" + "=json",
"sling.filter.scope" + "=REQUEST"
})
public class IntrospectionQueryFilter implements Filter {
public void init(FilterConfig filterConfig) {
// empty
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException {
if (!isAuthor() && servletRequest instanceof HttpServletRequest &&
servletResponse instanceof HttpServletResponse) {
final HttpServletRequest httpServletRequest =
(HttpServletRequest)servletRequest;
final HttpServletResponse httpServletResponse =
(HttpServletResponse) servletResponse;
HttpServletRequestWrapper requestWrapper =
new HttpServletRequestWrapper(httpServletRequest);
String graphQLBody = IOUtils.toString(requestWrapper.getInputStream(),
StandardCharsets.UTF_8);
if (StringUtils.isNotBlank(graphQLBody) &&
graphQLBody.contains("__schema")) {
httpServletResponse.sendError(SlingHttpServletResponse.SC_BAD_REQUEST);
return;
}
}
}
private boolean isAuthor() {
//ToDo – Add the logic to check the instance type, by run mode or configuration.
return true;
}